The Donut (aka Torus) Primitive: My Super Simple Avalon 3D Demo That Exercises The Mesh Primitives

As promised, I put together a very simple 3D sample that uses the Avalon 3D primitive class used in some other Avalon 3D samples. It turns out that TheRHogue already put together a sample with this class including code.  In fact, I would encourage you to check out all of his samples, although he only provides source to the initial sample.  He shows off some clever things, including how to manipulate the camera, so that if you click on one of the animations, you can use the Home, Page Up, End, Page Down, Delete and Insert keys to move the camera all around.

His samples nicely exercise the 3D capabilities in the WinHec build, but I wanted to put something together that is a bit simpler to follow, yet a bit more complex than the MSDN initial 3D sample or Ian Griffith's excellent article.  You can download the code from here.  However, the code is so small that I can fit into this blog post, minus the 3dMeshObjects class, which is in the .zip file

Basically, the sample displays each of the primitives in a window, the most interesting being the Donut primitive (aka the torus).  The app looks like this:

So, without futher adieu, let's discuss the code. Create an MSBUILD file called 3dprimitive.proj as such:

<Project DefaultTargets="Build">
<PropertyGroup>
<Property Language="C#" />
<Property AssemblyName="3dPrimitives" />
<Property TargetName="$(AssemblyName)" />
<Property RootNamespace="_3dPrimitives" />
</PropertyGroup>
<Import Project="$(LAPI)\WindowsApplication.target" />
<ItemGroup>
<Item Type="ApplicationDefinition" Include="MyApp.xaml" />
<Item Type="Pages" Include="Window1.xaml" />
</ItemGroup>
<ItemGroup>
<Item Type="Compile" Include="Mesh3DObjects.cs" SubType="Code" />
<Item Type="Compile" Include="MyApp.xaml.cs" DependentUpon="MyApp.xaml" SubType="Code" />
<Item Type="Compile" Include="Window1.xaml.cs" DependentUpon="Window1.xaml" SubType="Code" />
</ItemGroup>
</Project>

Then, we need a XAML file and csharp file to create our application model.  Call these MyApp.xaml and Myapp.xaml.cs

---------------------------------
MyApp.xaml
---------------------------------

<Application
xmlns="https://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
def:Class="_3dPrimitives.MyApp"
def:CodeBehind="MyApp.xaml.cs"
StartingUp="AppStartingUp"
>
</Application>

---------------------------------
MyApp.xaml.cs
---------------------------------

using System;
using System.Windows;

namespace _3dPrimitives
{
public partial class MyApp : Application
{
void AppStartingUp(object sender, StartingUpCancelEventArgs e)
{
Window mainWindow = new Window1();
mainWindow.Show();
}

    }
}

Lastly, let's look at our 3D code.  This will be Window1.xaml and Window1.xaml.cs.  We will keep the XAML as absolutely simple as possible.  At the top is a 2D area with a dock panel that has some buttons, which will load our 3d images.  Then, we will create a ViewPort3D with a perspective camera.  Notice that we aren't instantiating any models in our XAML; that will happen in code.

---------------------------------
Window1.xaml
---------------------------------

 

<Window
xmlns="https://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition"
xmlns:primitive="Control3DControl"
def:Class="_3dPrimitives.Window1"
def:CodeBehind="Window1.xaml.cs"
Text="_3dPrimitives"
>
<DockPanel DockPanel.Dock="Fill">
<DockPanel DockPanel.Dock="Top">
<Button Click="ButtonClickShowSphere">Sphere</Button>
<Button Click="ButtonClickShowCylinder">Cylinder</Button>
<Button Click="ButtonClickShowTorus">Torus</Button>
</DockPanel>
<ViewPort3D DockPanel.Dock="Fill" ClipToBounds="true" ID="myViewPort3D">
<ViewPort3D.Camera>
<PerspectiveCamera
Position="0,0,15.7"
LookAtPoint="0,0,15.6"
Up="0,1,0"
NearPlaneDistance="0.25"
FarPlaneDistance="20"
FieldOfView="40">
</PerspectiveCamera>
</ViewPort3D.Camera>
</ViewPort3D>
</DockPanel>
</Window>

 

Lastly, let's look at the code behind, only 35 lines.  First, we create some primitive factories using the primitives class.  We also create a material and a light class.  The remainder of the code is simply implementing our button click handlers, in which we do two things.  First, we create a MeshPrimitive3D, passing a mesh from one of our primitive factories and a material.  Second, we create a Model3DCollection, passing the MeshPrimitive3D and a light.  That's it!

---------------------------------
Window1.xaml.cs
---------------------------------

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media3D;
using Mesh3DObjects;

namespace _3dPrimitives
{
public partial class Window1 : Window
{
Mesh3DObjects.Sphere sphereFactory = new Mesh3DObjects.Sphere();
Mesh3DObjects.Torus torusFactory = new Mesh3DObjects.Torus(1,1);
Mesh3DObjects.Cylinder cylinderFactory = new Mesh3DObjects.Cylinder();
Material materialBeige = new BrushMaterial(new SolidColorBrush(Colors.Beige));
DirectionalLight light = new DirectionalLight(Colors.White, new Vector3D(-3, -4, -5));

  private void ButtonClickShowSphere(object sender, ClickEventArgs e)
{
MeshPrimitive3D sphere = new MeshPrimitive3D(sphereFactory.Mesh, materialBeige, null);
myViewPort3D.Models = new Model3DCollection(light, sphere);
}

  private void ButtonClickShowCylinder(object sender, ClickEventArgs e)
{
MeshPrimitive3D cylinder = new MeshPrimitive3D(cylinderFactory.Mesh, materialBeige, null);
myViewPort3D.Models = new Model3DCollection(light, cylinder);
}

  private void ButtonClickShowTorus(object sender, ClickEventArgs e)
{
MeshPrimitive3D torus = new MeshPrimitive3D(torusFactory.Mesh3D, materialBeige, null);
myViewPort3D.Models = new Model3DCollection(light, torus);
}
}
}