Click here to Skip to main content
15,885,985 members
Articles / Desktop Programming / WPF

Blend the OGRE Graphics Engine into your WPF projects

Rate me:
Please Sign up or sign in to vote.
4.85/5 (30 votes)
9 Sep 2008CPOL8 min read 183.6K   19.6K   68  
Blend a First Class Gaming Graphics Engine into your WPF application
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using Mogre;

namespace OgreLib
{
    public partial class OgreImage : D3DImage,
                                     ISupportInitialize
    {
        private delegate void MethodInvoker();

        private Root _root;
        private TexturePtr _texture;
        private RenderWindow _renderWindow;
        private Camera _camera;
        private Viewport _viewport;
        private SceneManager _sceneManager;
        private RenderTarget _renTarget;
        private int _reloadRenderTargetTime;
        private bool _imageSourceValid;
        private Thread _currentThread;
        private DispatcherTimer _timer;
        private bool _eventAttatched;

        #region IDisposable Members

        public void Dispose()
        {
            IsFrontBufferAvailableChanged -= _isFrontBufferAvailableChanged;

            DetachRenderTarget(true, true);

            if (_currentThread != null)
            {
                _currentThread.Abort();
            }

            if (_root != null)
            {
                DisposeRenderTarget();
                CompositorManager.Singleton.RemoveAll();

                _root.Dispose();
                _root = null;
            }

            GC.SuppressFinalize(this);
        }

        #endregion

        #region ISupportInitialize Members

        public void BeginInit()
        {
        }

        public void EndInit()
        {
            if (AutoInitialise)
            {
                InitOgre();
            }
        }

        #endregion

        protected bool _InitOgre()
        {
            lock (this)
            {
                IntPtr hWnd = IntPtr.Zero;

                foreach (PresentationSource source in PresentationSource.CurrentSources)
                {
                    var hwndSource = (source as HwndSource);
                    if (hwndSource != null)
                    {
                        hWnd = hwndSource.Handle;
                        break;
                    }
                }

                if (hWnd == IntPtr.Zero) return false;

                CallResourceItemLoaded(new ResourceLoadEventArgs("Engine", 0));

                // load the OGRE engine
                //
                _root = new Root();

                // configure resource paths from : "resources.cfg" file
                //
                var configFile = new ConfigFile();
                configFile.Load("resources.cfg", "\t:=", true);

                // Go through all sections & settings in the file
                //
                ConfigFile.SectionIterator seci = configFile.GetSectionIterator();

                // Normally we would use the foreach syntax, which enumerates the values, 
                // but in this case we need CurrentKey too;
                while (seci.MoveNext())
                {
                    string secName = seci.CurrentKey;

                    ConfigFile.SettingsMultiMap settings = seci.Current;
                    foreach (var pair in settings)
                    {
                        string typeName = pair.Key;
                        string archName = pair.Value;
                        ResourceGroupManager.Singleton.AddResourceLocation(archName, typeName, secName);
                    }
                }

                // Configures the application and creates the Window
                // A window HAS to be created, even though we'll never use it.
                //
                bool foundit = false;
                foreach (RenderSystem rs in _root.GetAvailableRenderers())
                {
                    if (rs == null) continue;

                    _root.RenderSystem = rs;
                    String rname = _root.RenderSystem.Name;
                    if (rname == "Direct3D9 Rendering Subsystem")
                    {
                        foundit = true;
                        break;
                    }
                }

                if (!foundit)
                    return false;

                _root.RenderSystem.SetConfigOption("Full Screen", "No");
                _root.RenderSystem.SetConfigOption("Video Mode", "640 x 480 @ 32-bit colour");

                _root.Initialise(false);

                var misc = new NameValuePairList();
                misc["externalWindowHandle"] = hWnd.ToString();
                _renderWindow = _root.CreateRenderWindow("OgreImageSource Windows", 0, 0, false, misc);
                _renderWindow.IsAutoUpdated = false;

                InitResourceLoad();
                ResourceGroupManager.Singleton.InitialiseAllResourceGroups();

                this.Dispatcher.Invoke(
                    (MethodInvoker)delegate
                                       {
                                           if (CreateDefaultScene)
                                           {
                                               //----------------------------------------------------- 
                                               // 4 Create the SceneManager
                                               // 
                                               //		ST_GENERIC = octree
                                               //		ST_EXTERIOR_CLOSE = simple terrain
                                               //		ST_EXTERIOR_FAR = nature terrain (depreciated)
                                               //		ST_EXTERIOR_REAL_FAR = paging landscape
                                               //		ST_INTERIOR = Quake3 BSP
                                               //----------------------------------------------------- 
                                               _sceneManager = _root.CreateSceneManager(SceneType.ST_GENERIC, "SceneMgr");
                                               _sceneManager.AmbientLight = new ColourValue(0.5f, 0.5f, 0.5f);

                                               //----------------------------------------------------- 
                                               // 5 Create the camera 
                                               //----------------------------------------------------- 
                                               _camera = _sceneManager.CreateCamera("DefaultCamera");
                                               _camera.Position = new Vector3(0f, 0f, 100f);
                                               // Look back along -Z
                                               _camera.LookAt(new Vector3(0f, 0f, -300f));
                                               _camera.NearClipDistance = 5;
                                           }

                                           IsFrontBufferAvailableChanged += _isFrontBufferAvailableChanged;

                                           if (Initialised != null)
                                               Initialised(this, new RoutedEventArgs());

                                           ReInitRenderTarget();
                                           AttachRenderTarget(true);

                                           OnFrameRateChanged(this.FrameRate);

                                           _currentThread = null;
                                       });

                return true;
            }
        }


        public bool InitOgre()
        {
            return _InitOgre();
        }

        public Thread InitOgreAsync(ThreadPriority priority, RoutedEventHandler completeHandler)
        {
            if (completeHandler != null)
                Initialised += completeHandler;

            _currentThread = new Thread(() => _InitOgre())
                                 {
                                     Priority = priority
                                 };
            _currentThread.Start();

            return _currentThread;
        }

        public void InitOgreAsync()
        {
            InitOgreAsync(ThreadPriority.Normal, null);
        }

        public event RoutedEventHandler Initialised;
        public event EventHandler PreRender;
        public event EventHandler PostRender;

        protected void RenderFrame()
        {
            if ((_camera != null) && (_viewport == null))
            {
                _viewport = _renTarget.AddViewport(_camera);
                _viewport.BackgroundColour = new ColourValue(0.0f, 0.0f, 0.0f, 0.0f);
            }

            if (PreRender != null)
                PreRender(this, EventArgs.Empty);

            _root.RenderOneFrame();

            if (PostRender != null)
                PostRender(this, EventArgs.Empty);
        }

        protected void DisposeRenderTarget()
        {
            if (_renTarget != null)
            {
                CompositorManager.Singleton.RemoveCompositorChain(_viewport);
                _renTarget.RemoveAllListeners();
                _renTarget.RemoveAllViewports();
                _root.RenderSystem.DestroyRenderTarget(_renTarget.Name);
                _renTarget = null;
                _viewport = null;
            }

            if (_texture != null)
            {
                TextureManager.Singleton.Remove(_texture.Handle);
                _texture.Dispose();
                _texture = null;
            }
        }

        protected void ReInitRenderTarget()
        {
            DetachRenderTarget(true, false);
            DisposeRenderTarget();

            _texture = TextureManager.Singleton.CreateManual(
                "OgreImageSource RenderTarget",
                ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME,
                TextureType.TEX_TYPE_2D,
                (uint)ViewportSize.Width, (uint)ViewportSize.Height,
                0, Mogre.PixelFormat.PF_A8R8G8B8,
                (int)TextureUsage.TU_RENDERTARGET);

            _renTarget = _texture.GetBuffer().GetRenderTarget();

            _reloadRenderTargetTime = 0;
        }

        public Root Root
        {
            get { return _root; }
        }

        public SceneManager SceneManager
        {
            get { return _sceneManager; }
        }

        public Camera Camera
        {
            get { return _camera; }
        }

        protected virtual void AttachRenderTarget(bool attachEvent)
        {
            if (!_imageSourceValid)
            {
                Lock();
                try
                {
                    IntPtr surface;
                    _renTarget.GetCustomAttribute("DDBACKBUFFER", out surface);
                    SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface);

                    _imageSourceValid = true;
                }
                finally
                {
                    Unlock();
                }
            }

            if (attachEvent)
                UpdateEvents(true);
        }

        protected virtual void DetachRenderTarget(bool detatchSurface, bool detatchEvent)
        {
            if (detatchSurface && _imageSourceValid)
            {
                Lock();
                SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
                Unlock();

                _imageSourceValid = false;
            }

            if (detatchEvent)
                UpdateEvents(false);
        }

        protected virtual void UpdateEvents(bool attach)
        {
            _eventAttatched = attach;
            if (attach)
            {
                if (_timer != null)
                    _timer.Tick += _rendering;
                else
                    CompositionTarget.Rendering += _rendering;
            }
            else
            {
                if (_timer != null)
                    _timer.Tick -= _rendering;
                else
                    CompositionTarget.Rendering -= _rendering;
            }
        }

        private void _isFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (IsFrontBufferAvailable)
                AttachRenderTarget(true); // might not succeed
            else
                // need to keep old surface attached because it's the only way to get the front buffer active event.
                DetachRenderTarget(false, true);
        }

        private void _rendering(object sender, EventArgs e)
        {
            if (_root == null) return;

            if (IsFrontBufferAvailable)
            {
                if (MogreWpf.Interop.D3D9RenderSystem.IsDeviceLost(_renderWindow))
                {
                    _renderWindow.Update(); // try restore
                    _reloadRenderTargetTime = -1;

                    if (MogreWpf.Interop.D3D9RenderSystem.IsDeviceLost(_renderWindow))
                        return;
                }

                long durationTicks = ResizeRenderTargetDelay.TimeSpan.Ticks;

                // if the new next ReInitRenderTarget() interval is up
                if (((_reloadRenderTargetTime < 0) || (durationTicks <= 0))
                    // negative time will be reloaded immediatly
                    ||
                    ((_reloadRenderTargetTime > 0) &&
                     (Environment.TickCount >= (_reloadRenderTargetTime + durationTicks))))
                {
                    ReInitRenderTarget();
                }

                if (!_imageSourceValid)
                    AttachRenderTarget(false);

                Lock();
                RenderFrame();
                AddDirtyRect(new Int32Rect(0, 0, PixelWidth, PixelHeight));
                Unlock();
            }
        }

        private void OnFrameRateChanged(int? newFrameRate)
        {
            bool wasAttached = _eventAttatched;
            UpdateEvents(false);

            if (newFrameRate == null)
            {
                if (_timer != null)
                {
                    _timer.Tick -= _rendering;
                    _timer.Stop();
                    _timer = null;
                }
            }
            else
            {
                if (_timer == null)
                    _timer = new DispatcherTimer();

                _timer.Interval = new TimeSpan(1000 / newFrameRate.Value);
                _timer.Start();
            }

            if (wasAttached)
                UpdateEvents(true);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead FNB Connect
South Africa South Africa
iOS Technical Lead at FNB
-

Computers are really sweet. Aren't they?
Yup they are...

I've always loved writing tools and components...never been very interested in playing games....always wanted to be able to write them though.

And, yes. I'm still pretty annoyed they discontinued the Amiga computer.

Comments and Discussions