Monday, April 11, 2011

Silverlight 5: 3D Physics Demo

Silverlight 5 introduces new support for hardware accelerated 3D which makes it possible to create truly unique experiences. Unfortunately, the 3D API provided is very low-level, and probably not easily picked up by the average developer.
In this blog post, I'll show you how to leverage existing libraries and tools to (relatively) easily create a 3D Physics based game or simulation using the new 3D features of Silverlight 5.

To accomplish our 3d scene and physics, we'll be using two open source libraries:
1.       Ba lder 3D Engine: this engine was created by Einar Ingebrigtsen and allows Silverlight to load and display 3D models. Balder has been around for quite some time, but because of the lack of native 3D support in previous versions of Silverlight, performance was very limited. With the addition of native 3D support in Silverlight 5, much more is possible with Balder.

2.       JigLibX Physics library: this 3D physics engine has had many incarnations. It started as a C++ physics library, and was later ported to C# and XNA. Since there is no official Silverlight version of JigLibX, I created a slightly modified version of JigLibX with thanks to the great start on this work item.

Creating the 3D Models

There are so many different 3D model formats out there, it can be quite overwhelming. 3D Studio (.3DS), Wavefront (.OBJ) and Lightwave (.MDD) are just a few. Balder requires a model be in an ASE format (ASCII Scene Exporter), which is a popular format for 3D game frameworks.
Many tools can convert to ASE format, including the free Blender modeling software. You can download Blender 2.5 Beta
here, and there is a Python script for Blender which enables ASE exports here. Also, MilkShape exports to ASE and can import a bunch of different formats and is just $35 US.
Balder also supports texture mapping of ASE models, but just be sure the texture is in either JPG or PNG format (often, modeling software uses a BMP format which Balder cannot display). You also need to make sure the reference inside the ASE file is correct for the texture file.  ASE files are just plain text, so you can open up the file in an editor and look for any BITMAP references like this:       &n bsp;             *BITMAP "crate.jpg"

Displaying the Model

Once you have a model in ASE format, we can display this in Silverlight using Balder. The author of Balder has a great Getting Started Video which I recommend you watch if you are new to Balder. This video was created for Silverlight 4, so there are a couple of notes to keep in mind when using Silverlight 5 and the new version of Balder:
·         Be sure to Enable GPU Acceleration. This is required for Silverlight 5 to display 3D scenes. Add the following to the parameters of the Silverlight plug-in object:<param name="enableGPUAcceleration" value="true" />

·         Sometimes you may see a "white screen of death" - that is, nothing is rendered to the window when using 3D. You can use the following properties to determine what's up when that happens (but usually it is because you didn't enable GPU acceleration as above).Microsoft.Xna.Framework.Silverlight.GraphicsDeviceManager.Current.RenderMode

Adding Physics

The job of the physics engine is to determine collisions, position, rotation, and the forces upon an object. We then take that position and rotation informati on and update our Balder 3D objects each frame.
I chose to use JiglibX as the physics engine, but there are several others that will no doubt make their way to Silverlight 5 in the near future. To get a quick overview of how JiglibX works, I recommend using the Basic World Tutorial.
If you take a look at the sample code, you will see that I sub-classed several of the Balder Geometries into Physics based classes. These wrap together both the visual Balder model and the JiglibX physics logic:
·         PhysicsB ox displays a cube shape and is based on Balder.Objects.Geometries.Box. On the physics side, this uses the JigLibX.Geometry.Box class.

·         PhysicsCapsule displays a capsule model, like a pill shape. There is a special model for this, Capsule.ase. On the physics side, this uses the JigLibX.Geometry.Capsule class.

·         PhysicsRagDoll is the most complicated class, and creates a rag doll based on a whole bunch of other primitive objects including sphere, capsule, and box. This class was ported from the original JiglibX demo game.

·         PhysicsSphere displays a sphere model, using the sphere.ASE model. Note that Balder does not have a sphere primitive at this time, so that is why we need to bring in a custom model. On the physics side, this uses the JigLibX.Geometry.Sphere class.

Each of these classes implements an IPhysicsObject interface, which provides a Draw() method. This way we have a common way of updating the visual model with the underlying physics library data.

Adding Camera Control

For controlling the camera, I converted a class provided in this XNA Tutorial. This tutorial by Pete Street walks through basic camera control, so I suggest you read through it for the details. After the Camera class does its magic, we simply need to tell the Balder Camera where to move and point towards. This is done in the UpdateViewMatrix method of the Camera class:_gameCamera.Position = Utils.VectorXnaToBalder(position);
_gameCamera.Target = Utils.VectorXnaToBalder(target);

Terrain Mapping

Terrains in a 3D physics game are often created using a Heightmap.  A heightmap is just a 2D bitmap image, where brighter pixel values represent higher elevation than dimmer pixel values. This 2D im age data is then handed off to a 3D engine (or physics engine), and a corresponding model is created from the map. If you want to try creating a random heightmap, you can use a tool like Paint.NET to render clouds (in Paint.NET, just create a new image and select Effects/Render/Clouds).
Both Balder and JiglibX have support for heightmaps, so we are in luck! In the sample code you can see there is a PhysicsHeightMap class to handle feeding height data to a JiglibX Heightmap Collision Skin. The Balder Heightmap object is fed the same data through its HeightInput event in the MyGame class. I had some issues instantiating a Balder heightmap from code, so I designed the PhysicsHeightMap class to instead accept an existing visual heightmap object.


Although 3D in Silverlight 5 is very low level, we don't have to be 3D experts to implement fantastic experiences using this new API. Instead, we can leverage existing open source libraries to get a quick start on 3D applications.
These basic 3D capabilities of Silverlight 5 borrow a great deal from the XNA Game Library. One has to wonder if all of XNA will eventually be implemented in Silverlight, making it a powerful development framework for everything from 3D games to business applications!