DotWarp - Server-side 3D renderer for .NET

Skip to the end...

You can download DotWarp binaries from two places:

What is it?

Building on top of Nexus and Meshellator, I am pleased to announce the first version of DotWarp, an open source server-side 3D renderer for .NET 4.0. It can be used in a background application (i.e. one without a UI) for rendering 3D meshes into 2D bitmaps.

As the name suggests, DotWarp builds on top of Windows Advanced Rasterization Platform (or WARP). WARP is a software rasterizer, written by Microsoft, which allows Direct3D to be used in situations where it previously could not be - such as on servers without dedicated graphics hardware. WARP will work wherever the DirectX 11 runtime is installed, which at this time is Windows 7, Windows Server 2008 R2, and Windows Vista with the KB971644 update. I have personally tested it on Windows 7 and Windows Server 2008 R2.

DotWarp also makes extensive use of SharpDX. SharpDX is a .NET managed DirectX API, directly generated from DirectX SDK headers. I could also have used SlimDX, but I like the fact that SharpDX is platform independent, so doesn't require separate builds for x86 and x64. I'd like to give a shout out to Alexandre Mutel, not just for writing SharpDX but also for being very helpful answering my (in some cases dumb) questions about it.

How can I use it?

First you'll need to add a reference from your project to the DotWarp, Meshellator and Nexus assemblies. The easiest way to do this is via NuGet (just search for DotWarp - that package will bring in all required dependencies), but you can also download the binaries from GitHub. You'll also need the latest DirectX End-User Runtime.

The only DotWarp class you need to worry about is WarpSceneRenderer. This requires a Scene object, a width and height, and a Camera. The Scene object comes from Meshellator (either loaded from a mesh file or created from a primitive shape), and the Camera comes from Nexus (either created manually or automatically from the scene bounds). The Render method returns a BitmapSource, which is WPF's bitmap type. This can then be saved to disk, or used however you like.

We'll start simple:

Scene scene = MeshellatorLoader.CreateFromTeapot(10, 20);
using (WarpSceneRenderer renderer = new WarpSceneRenderer(scene, 550, 350))
{
    renderer.Initialize();

    Camera camera = PerspectiveCamera.CreateFromBounds(
        scene.Bounds, new Viewport(0, 0, 550, 350),
        MathUtility.PI_OVER_4, 0, 0, 1);

    BitmapSource bitmap = renderer.Render(camera);

    JpegBitmapEncoder e = new JpegBitmapEncoder();
    e.Frames.Add(BitmapFrame.Create(bitmap));
    using (Stream stream = File.OpenWrite("output.jpg"))
        e.Save(stream);
}

Which results in this, which is not terribly interesting:

image

To control the camera, we can setup a PerspectiveCamera or OrthographicCamera with the desired parameters. But it's hard to get the position and direction right, and I'm lazy, so I like to use the CreateFromBounds method on PerspectiveCamera, which lets you set yaw and pitch like this (angles are in radians):

...

Camera camera = PerspectiveCamera.CreateFromBounds(
    scene.Bounds, new Viewport(0, 0, 550, 350),
    MathUtility.PI_OVER_4, 0.3f, 0.3f, 1);

...

Keeping everything else the same, results in this image, which is slightly better:

image

Of course only rendering primitives is not really very useful, so let's render a mesh from a 3DS file:

Scene scene = MeshellatorLoader.ImportFromFile("Models/3ds/85-nissan-fairlady.3ds");
using (WarpSceneRenderer renderer = new WarpSceneRenderer(scene, 550, 250))
{
    renderer.Initialize();

    Camera camera = PerspectiveCamera.CreateFromBounds(
        scene.Bounds, new Viewport(0, 0, 550, 250),
        MathUtility.PI_OVER_4, 2.5f, -0.3f, 1);

    BitmapSource bitmap = renderer.Render(camera);

    JpegBitmapEncoder e = new JpegBitmapEncoder();
    e.Frames.Add(BitmapFrame.Create(bitmap));
    using (Stream stream = File.OpenWrite("output.jpg"))
        e.Save(stream);
}

That's a little more interesting:

image

There are a few options that can be configured (not many options in this first version):

Scene scene = MeshellatorLoader.ImportFromFile("Models/Obj/Tank.obj");
using (WarpSceneRenderer renderer = new WarpSceneRenderer(scene, 550, 350))
{
    renderer.Initialize();

    renderer.Options.TriangleWindingOrderReversed = true;
    renderer.Options.BackgroundColor = Colors.Gray;
    renderer.Options.LightingEnabled = false;

    Camera camera = PerspectiveCamera.CreateFromBounds(
        scene.Bounds, new Viewport(0, 0, 550, 350),
        MathUtility.PI_OVER_4, 0.5f, -0.3f, 1);

    BitmapSource bitmap = renderer.Render(camera);

    JpegBitmapEncoder e = new JpegBitmapEncoder();
    e.Frames.Add(BitmapFrame.Create(bitmap));
    using (Stream stream = File.OpenWrite("output.jpg"))
        e.Save(stream);
}

image

Finally, the camera can of course be positioned manually:

Scene scene = MeshellatorLoader.ImportFromFile("Models/3ds/85-nissan-fairlady.3ds");
using (WarpSceneRenderer renderer = new WarpSceneRenderer(scene, 550, 350))
{
    renderer.Initialize();

    renderer.Options.BackgroundColor = Color.FromRgb(200, 200, 200);

    Camera camera = new PerspectiveCamera
    {
        LookDirection = new Vector3D(-1, -0.3f, 1),
        Position = new Point3D(2100, 1200, -700),
    };

    BitmapSource bitmap = renderer.Render(camera);

    JpegBitmapEncoder e = new JpegBitmapEncoder();
    e.Frames.Add(BitmapFrame.Create(bitmap));
    using (Stream stream = File.OpenWrite("output.jpg"))
        e.Save(stream);
}

image

DotWarp can also be used from the command line, but the command line tool only lets you specify an input model and an output file, and it positions the camera automatically, so it's not terribly useful for anything other than testing:

DotWarp.CommandLine.exe "MyModel.3ds" "output.png"

And that's it! I hope you find this useful - if you have any feedback or suggestions, please let me know with a comment below.

6 comments

Gravatar Lucky Me
Apr 9, 2011
23:46

Great work !!

Gravatar Tim Jones
Apr 13, 2011
11:43

Thanks!

For right now, I don't have any other examples. You can have a look inside the Scene object; it should be quite clear what's going on, but let me know if you have specific questions.

I have a lot I would like to do with DotWarp, but right now I don't have time. I have committed some changes to the repo which will allow for point lights and spotlights, but I haven't actually implemented those light types yet.

Gravatar Lloyd Dupont
May 9, 2011
22:41

Nice work! :)

I'm learning DirectX (in C#!) now!... Reading this DirectX tutorial: http://msdn.microsoft.com/en-us/library/ee418800(VS.85).aspx

I see that mesh animation is done through a hierarchy of mesh (if I may say, I might use a wrong terminology being a newbie! ;) Anyway, looking at the Scene and Mesh class in Meshellator I see no such hierarchy!

Did I miss something? Would it be something you might implement one day?

Gravatar Tim Jones
May 23, 2011
22:59

@Lloyd, you didn't miss anything - Meshellator doesn't (yet) have any concept of scene hierarchies. I see you've created an issue for it on GitHub - thank you, that will remind me to add the feature when I have some free time.

Gravatar David Snipp
Dec 11, 2011
21:24

Latest github version of DotWarp doesn't build with latest github version of SharpDX.

DotWarp form nuget fails inside renderer.Initialize with my ATI Radeon HD 5450 hardware:

_renderTexture = new Texture2D(_device, _renderTextureDescription);

with Error: E_INVALIDARG (0x80070057) An invalid parameter was passed to the returning function

It does work, however, when you use Device.CreateWithSwapChain. So, my intention was to modify DotWarp.Initialize to use this, but I can't compile it against the latest SharpDX.

There also has to be a better way to get the buffer besides one pixel at a time inside PopulateBitmap. This would appear to be very slow.

Gravatar faisal
Dec 16, 2011
09:39

Hi, I am working on my final year project, i am getting this error

The best overloaded method match for 'DotWarp.WarpSceneRenderer.Render(Nexus.Graphics.Cameras.Camera)' has some invalid arguments

can u help me figure out what is wrong,

Thanks....

Make a comment

Sorry, commenting has been temporary disabled because of spam. If you have any questions, you can email me, and you can also find me on Twitter.