Using Ogre and .NET

From EQUIS Lab Wiki

Revision as of 14:48, 15 April 2011 by Graham (Talk | contribs)
(diff) ← Older revision | Current revision (diff) | Newer revision → (diff)
Jump to: navigation, search


To read about Rob and Banani's ongoing adventures in learning Managed C++ and getting LIAV ready for workspace, that can be found here.

Ogre is a C++ library that is available across a few different platforms (Windows, Linux, etc.). It can be used both by C++ applications (it's written in C++), and also has some bindings to a few other high level languages. For our work, we're interested in integrating Ogre into the .NET architecture in a way that gives us both the speed and fine grained control afforded by C++, but also integrates with components written in other, more friendly languages such as C#.

To explore some possible approaches, we attempted to develop a simple application that accepts some simple commands from a C# form and performs a change to the scene rendered by Ogre. Specifically, we want to choose the color of the ambient light in the scene from a list on a C# form.

The following are some approaches we've looked at to accomplish this:

Contents

A .NET port of Ogre

Axiom is a project aiming to provide a .NET port of Ogre. We would use Axiom as a direct replacement for Ogre and write all our scene management in C# rather than C++.

Advantages

  • C# is a much nicer language to use than C++ and we would get a nice productivity gain.
  • Since we would like to use C# for other application components, we're already commiting to the .NET framework. So it makes sense to try and incorporate core components such as the graphics renderer directly into the runtime. If we could get everything in there, we could conceivably run cross-platform on any machine with a CLR.

Disadvantages

  • Using a port of a big, actively maintained library like Ogre has some inherent problems. Most notably, a latency arises when a new feature gets added to Ogre. It will only be available in Axiom when the project members get it ported to C#, and that port process won't begin until the new feature is added to the core library.
  • Also, graphics rendering is a component from which we will require high performance. Running this part in interpreted C# code will introduce performance problems.
  • The big problem is that Axiom appears to be pretty much dead now, probably for these reasons.

Verdict

Might be usable for some simple things, but not to our scale.

Details

This option was never strongly considered since the library isn't really maintained anymore and has some missing features. We tried to download it just to try it out and had a bit of a hard time locating it. We read a number of reviews that suggested the productivity increase afforded by using C# was nice, but the performance problems are enough that it would be difficult to do anything with heavy rendering requirements. We never made much of an effort to try it out ourselves.

.NET language bindings to Ogre

OgreDotNet provides a binding to the Ogre libraries from .NET, but does not actually port anything. This interface is generated using SWIG. The same approach is now being used to generate bindings for other languages such as Java and Python.

Advantages

  • Ogre runs natively, so there's little performance impact on the rendering pipeline.
  • The maintenance problem with the Ogre port is reduced since only an interface specification needs to be maintained.
  • This project is quite active and the development process appears to be closely coupled with the Ogre development process itself.

Disadvantages

  • The bindings introduce a small performance hit when a lot of transformations to the scene graph happen all at once.
  • Ogre likes to use inheritance a lot to provide fine-grained control over the rendering process. The type system for the language bindings does not allow for overloading.
  • Project is relatively new and still a bit under construction.

Verdict

Could work well for simple to advanced things, but not to our scale.

Details

This approach appeared promising, so I (Dave) downloaded it from the Ogre website. They don't have a binary build for it, so you get a Visual Studio .NET 2003 solution that is essentially a bunch of text files that describe the interface and a build file. You must have Ogre, SWIG, and .NET Studio installed on your machine, and then you build your solution from within .NET Studio. The result is supposed to be a DLL that you can then link into your own solution along with the Ogre libraries.

I tried going through the setup process 3 times and never got SWIG to successfully build the DLL file. SWIG would generate a bunch of code based on the interface files, but then that code would not compile. This is a fairly new project and the documentation is a bit lacking at this point, and I couldn't find any mention of the compilation problems I was having. Maybe one day they'll release a prebuilt binary of the interface.

While trying to debug my build problem, I read a bit about the limitations of using these bindings. The biggest one is that you can't inherit from an Ogre class and have overloaded functions called. This is a big deal since at a minimum we would like to implement our own FrameListeners.

Referencing Ogre in Managed C++

.NET allows components to be written in a dialect of C++ called "Managed C++". With this, you can reference both C++ libraries and .NET components. Additionally, Managed C++ has a garbage collector.

Advantages

  • Fine grained control over the Ogre rendering pipeline
  • Some productivity gain over basic C++
  • Easy integration with other .NET components

Disadvantages

  • Rendering performance is reduced since the runtime is interpreted and has a garbage collector
  • The type system is somewhat limited when you want to a managed class to inherit from an unmanaged class.
  • While the runtime will be totally .NET, it will link to native code which would require different builds for different platforms.

Verdict

Could work well for simple to advanced things, but not to our scale.

Details

I tried creating a renderer in Managed C++ while making use of the language features afforded by the .NET platform. For the most part this worked out okay when 2 conditions were met:

  • No classes needed to inherit from an Ogre class
  • No Ogre objects would be members of a Managed C++ class

Apparently the director has a hard time with the first case and the garbage collector has a hard time with the second. These limitations pretty much reduce this approach to the same effectiveness as the binding approach, but without the advantage of using C#.

Referencing an unmanaged C++ component from Managed C++

.NET allows pointers to unmanaged C++ classes to be maintained by Managed C++ classes. This allows us to build the graphics component of the application in unmanaged C++ and interface with it through Managed C++.

Advantages

  • Full flexibility when working with Ogre
  • Optimal rendering performance

Disadvantages

  • Unmanaged C++ is more difficult to develop with
  • Can't access .NET components in the unmanaged components
  • More complicated architecture since interface between .NET components and the graphics component must be mediated by a Managed C++ layer

Verdict

Overkill for simple and some advanced projects, but excellent for large, high-performance projects. This is the approach we will use.

Details

This is the last and most successful approach I (Dave) tried. I put it together in 3 components:

  • A C# form with a "Start" button and some radio buttons with color choices for the ambient light
  • An unmanaged C++ class that contained a bunch of code to get Ogre running and draw a scene with a small robot and some ambient light. This class had functions to start up the renderer and to change the color of the ambient light
  • A managed C++ class with a pointer to an instance of the unmanaged class.

The C# class had an instance of the managed class as a member. When the user clicked "Start", the managed class would call the "start" function of the unmanaged class and everything seemed to be happy with this. Then when the user would change the color of the light on the C# form, the managed class just called the unmanaged class's color changing function and it works great!

Procedure

The more detailed procedure is the following:

  • Create a solution for your program
  • You will need two projects within this solution -- one for the C++ code (both managed and unmanaged), and one for the C# code

C++ Project

  • Create the C++ project as a CLR Class Library
  • Create a managed C++ class to wrap the unmanaged code. This class will provide the interface you use in the C# part of your program. Put this class in a namespace.
    • You specify that a C++ class is managed simply with the syntax ref class Foo { ... }
    • Managed C++ is different from standard C++ -- for example, it provides garbage-collected memory management and safe pointers, with different syntax from standard C++. See details here.
    • Passing parameters between managed and unmanaged code may require conversions. See details here.
  • Place your unmanaged C++ code in the same project. Your managed C++ class can call unmanaged code as normal (module type conversion issues.)
  • When you build this project, the result will be a dll

C# Project

  • Create a C# project
  • To make your C++ class accessible, right click on the project and Add Reference. Use the Project tab to select the project you created above.
  • In your C# code, simply include using N at the beginning classes where you wish to use the C++ code, where N is the namespace you used in the C++ code. You can then declare and use an instance of your C++ class.