Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Blend the OGRE Graphics Engine into your WPF projects

0.00/5 (No votes)
9 Sep 2008 1  
Blend a First Class Gaming Graphics Engine into your WPF application

I have uploaded the demo project in 2 parts. (Sorry :-( OGRE is quite large and I couldn't upload 1 file.)
Extract both of the zip files into the same place before running the demo.
e.g. in winzip click "extract" -> "c:\temp" (same process for both)
Your directory structure should look like this once completed doing so:
..\OgreInWpf\DemoApps
..\OgreInWpf\Libraries
..\OgreInWpf\Media
(MUST EXIST)
..\OgreInWpf\Release (goto this folder to run the demos)

With This You Can... the 10 second overview

  • Blend a First Class Gaming Graphics Engine into a First Class Presentation System.
  • Spill your coffee over your keyboard as you see just how cool your UI look and feel can be.

References

Requirements

  • Visual Studio 2008
  • .Net framework 3.5 (Service Pack 1)
  • A reasonable graphics card. This project might not run on all computers. I have only tested it on NVidia cards.

Introduction

The Viewport3D control in the WPF is pretty useful, but it lacks the cool tweakiness of a proper graphics engine, e.g:

  • performance critical features like scene paging and "level of detail" meshes allowing large meshes to be displayed using different configurations; a hi-polygon version when near to the camera for when high definition is important, and with a low-polygon version when far away from the camera.
  • GPU (Graphics Processor) scripts for DirectX Pixel Shaders to achieve scene blurring, or to simulate night vision goggles. Now, I'm not talking about the GPU code itself, because the new 3.5 SP1 framework has got GPU Bitmap effects, I'm talking about making it easy and maintainable to apply these kinky features by having a scripting resource mechanism so you don't even have to write a line of code to include fancy first rate materials features in your scene. (Well one line ;-) This allows you to change the object's materials easily and without any re-compiling. No mess, no fuss.
  • Proper package resource management for your textures, scripts, GPU Shaders, animations etc.
  • Plenty of other stuff which you'll need, even before you get to the physics, sound, AI and actual gaming side...

As you can see there really is a lot of technology that goes into a modern game and animation effects. Now, graphics aside what about all this behaving itself on your desktop, reading office documents, allowing a user to work in an interface that's funky, themable, supports data binding, web services, swanky smoke effects, etc...

Missing the good old days of Pac-man and DOS ?. It all seemed so much simpler back then. I know I sometimes do.... um, miss DOS, not seem simple. Though I know people that might argue that point.

Not convinced...ok, ok, say your boss comes to you and says, "Frank", cause that's your name...you know, or Bob.
"Frank" he says, "we need to write an application for the 'Smokey Cheese Company', that has, ok, get this, a swift little smoke effect on the main screen, see, bellowing smoke into the air -", (I can do this you say to yourself, you know, graphics isn't too hard) ...", the boss continues, " - over their scrolling logo and displays a list of stuff from a web service where the user can click - "
Ok now it's getting a bit harder all of a sudden because graphics engines don't have very good UI controls (the boss still hasn't stopped talking by the way)
- "..blah blah, by Friday!", the boss finishes.
Unfortunately Bob is out for tea.

The Fluffy Bunny Of Doom syndrome.

Each a do-able task in their own right?. Integration people, that's what I'm talking about.

Enter Stage (left): The OgreImage Class

Ogre loading

Sweet particle effects

Realtime TV compositor effect

The OgreImage class is just like any of the other WPF ImageSource classes. An image source can be used as a brush for a background, for text, or displayed in an Image control and the OgreImage is no different, except that it has all the functionality of a First Class Graphics Engine. It has been based on the new D3DImage (system.windows.interop.d3dimage.aspx) class which is part of the Framework 3.5 SP 1 update.

I have, in the simplest way implemented the process of hosting the OGRE engine in the DirectX context of the D3DImage class. That is to say, you will still need to interact with the OGRE library via the MOGRE library as any other .NET OGRE application, but won't have to deal with screen re-sizing and DirectX device loss/management issues. One thing I have implemented is asynchronous loading of the OGRE resources and have included a progress event system to allow for the a progress indicator implementation (see example). I've done this because OGRE Engine can take a while to start-up.

There is currently no implementation of a control similar to the Viewport3D, but I'm hoping to expand on this project and to xaml-fy it more to allow for the entire scene to be constructed in xaml.

How To Use It

Exactly like you might have displayed a bitmap in the Image control you could do the following:

The ImageSource

<Image x:Name="RenterTargetControl" 
       Stretch="Fill"
       SizeChanged="RenterTargetControl_SizeChanged" 
       MouseDown="RenterTargetControl_MouseDown"
    <Image.Source>
        <OgreLib:OgreImage x:Name="_ogreImage"
                           Initialised="_image_InitScene" 
                           PreRender="_image_PreRender"
                           ResourceLoadItemProgress=
                            "OgreImage_ResourceLoadItemProgress"/>
    </Image.Source>
</Image>

This would include the OgreImage class in your page displayed via the Image control. The events of interest are:

  • The ResourceLoadItemProgress event which is called for every resource loaded. Here you can get the name of the resource and a scalar (0 -> 1) indicating the progress of the entire loading process.

    private void OgreImage_ResourceLoadItemProgress
    	(object sender, ResourceLoadEventArgs e)
    {
        _progressName.Text = e.Name;
        
        // scale the progress bar
        _progressScale.ScaleX = e.Progress;
    }
  • The Initialised event. This event is called just after all the resources have been loaded (and before the first frame is rendered). In this event the scene can be constructed.

    Please refer to the Ogre wiki on how to use Ogre SceneManager itself.

    void _image_InitScene(object sender, RoutedEventArgs e)
    {
        // start the scene fade in animation
        var sb = (Storyboard)this.Resources["StartEngine"];
        sb.Begin(this);
    
        // get the Ogre scene manager
        SceneManager sceneMgr = _ogreImage.SceneManager;
    
        // create a light
        Light l = sceneMgr.CreateLight("MainLight");
    
        // Accept default settings: point light, white diffuse, just set position
        // NB I could attach the light to a SceneNode 
        // if I wanted it to move automatically with
        // other objects.
        l.Position = new Vector3(20F, 80F, 50F);
    
        // load the "ogre head mesh" resource.
        _ogreMesh = sceneMgr.CreateEntity
    		("ogre", "ogrehead.mesh");
    
        // create a node for the "ogre head mesh"
        _ogreNode = sceneMgr.RootSceneNode.CreateChildSceneNode
    		("ogreNode");
        _ogreNode.AttachObject(_ogreMesh);
    
        // Create shared node for the particle effects
        _fountainNode = sceneMgr.RootSceneNode.CreateChildSceneNode();
    
        // Set nonvisible timeout
        ParticleSystem.DefaultNonVisibleUpdateTimeout = 5;
    
        // update the partical systems
        _cb_Click(null, null);
    }
  • The PreRender event. This event is called before the frame is rendered. Here you can update or change any of the scene nodes.

    void _image_PreRender(object sender, System.EventArgs e)
    {
        // if the viewport has changed reload the special effects
        if (_ogreImage.Camera.Viewport != _viewport)
        {
            _viewport = _ogreImage.Camera.Viewport;
    
            _cbCompositor_Click(_cbBloom, null);
            _cbCompositor_Click(_cbGlass, null);
            _cbCompositor_Click(_cbOldTV, null);
        }
        
        // rotate the "ogre head"
        _ogreNode.Rotate(new Vector3(0, 1, 0), new Degree(0.1f));
    }

Resizing the Ogre Viewport

Also, to get the Ogre viewport to resize itself when the Image size changes you need to manually resize the OgreImage image on the size event of the Image.

private void RenterTargetControl_SizeChanged
	(object sender, SizeChangedEventArgs e)
{
    if (_ogreImage == null) return;

    _ogreImage.ViewportSize = e.NewSize;
}

To keep the app responsive during a window drag exercise I've made the OgreImage class delay its processing of the resize. This will make the image wait for a couple of milliseconds (200 by default) until the size has stabilized. Have a look at the ResizeRenderTargetDelay property.

Starting it all. "Graphics, you haz dem."

One more thing to do is to start the engine. Currently this is done manually by calling the InitOgreAsync method. The reason why the engine can't start automatically is because it requires a valid windows handle to initialize itself and this isn't available while the page xaml is busy loading. I'm sure it won't be too difficult to come up with a clever solution, but I'll let you guys do that. ;-)

The control can be initialized on the window's Loaded event.

 private void Window_Loaded(object sender, RoutedEventArgs e)
{
    _ogreImage = (OgreImage)RenterTargetControl.Source;
    _ogreImage.InitOgreAsync();
}

Binding to the OGRE Library and Media

The OGRE library is quite a monster to get setup for a project. I have only included the essential DLLs and media in this article's download because the full MOGRE setup is quite large. If you would like to develop using MOGRE you will need to download the full SDK which is a hefty 40 meg.

The executables in this project have been compiled into the "Release" folder of the article's download where all the OGRE/MOGRE "Release" DLLs and resource config files are. They are quite large so there's only one instance of them. That is to say, all the VS projects have been configured to have their build output go directly into the Release folder.

If you'd like to re-compile the MogreWpf.Interop.dll (a c++ project) you'll need to set the MOGRE_HOME environment variable in windows to include the "<..>\MogreSDK" path....once you've downloaded the MOGRE SDK. Also compile it in "Release" mode to match the Ogre and Mogre DLLs for this project's download.

Points of Interest

  • The WPF now supports GPU Bitmap effects (a-series-on-gpu-based-effects-for-wpf)
    This article doesn't use them, it just might interest you to know that it's now possible to do funky stuff in the WPF.
  • Ogre is a pretty good object oriented graphics library. If you like graphics programming you should have a look at it. It is mature library, is fun to use and has a good Texture Material and Particle Effects scripting language.

Conclusion

And there you have it. A 3D graphics engine blended into your WPF window. Ooooooh,....shiny!

Ok, this wouldn't be the best way to create a 2000 "frames per second" game. It would be best to leave the OGRE Engine to use its native windowing system, but it will keep your boss loving you at your day job. ;-)

Note: Before you make all kinds of promises to 'said' boss, read the OGRE and MOGRE licenses first, and also I'm not sure the implementation of the OgreImage is 100% reliable at all times. I have had some funny crashes, though it seems pretty stable at the moment after my last code tweak'ins (fluffy bunnies aside). However it should give you a good place to start your "next level" graphics journey.

History

  • 2008-09-09 - Initial publication

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here