3D in XAML/C#: Independent Control of Multiple 3D Models

A short program that shows how to use 3DModelGroups, Animation and camera position control.

3d Model Groups, Animation and Camera Control

This was the second 3D program using XAML and C# that I attempted (and succeeded) after reading the only 3D example in the book I am learning from. The example was a rotating 3D pyramid, but I didn't type it in the way it was. I decided instead to modify it and expand it to do more stuff with more stuff.

I had already figured out how to do a rotating 3D cube from looking at the example in the book, so I wanted to try something more complex. I got out some graph paper and drew a large 3D cross and plotted the points. I don't have a 3D modeling program so I did it the old-school way. I placed all the points into the MeshGeometry3D and figured out the TriangleIndices I needed.

 <!-- a large 3-D cross, centered at origin 0,0,0 -->
   <GeometryModel3D.Geometry>
       <MeshGeometry3D

          Positions="1,-1,-4   1, 1,-4  -1,-1,-4  -1, 1,-4
                     1,-1,-2   1, 1,-2  -1,-1,-2  -1, 1,-2
                     1,-3, 0   1,-1, 0  -1,-3, 0  -1,-1, 0
                     1, 1, 0   1, 3, 0  -1, 1, 0  -1, 3, 0
                     1,-3, 2   1,-1, 2  -1,-3, 2  -1,-1, 2
                     1, 1, 2   1, 3, 2  -1, 1, 2  -1, 3, 2
                     1,-1, 4   1, 1, 4  -1,-1, 4  -1, 1, 4"
                  

           TriangleIndices="

                     0, 1, 4   1, 5, 4    2, 0, 6   0, 4, 6
                     1, 3, 5   3, 7, 5    3, 2, 7   2, 6, 7
                     2, 3, 0   3, 1, 0    4, 5, 9   5,12, 9
                     6, 4,11   4, 9,11    1, 7,12   7,14,12
                     7, 6,14   6,11,14    8, 9,16   9,17,16
                    10, 8,18   8,16,18   11,10,19  10,18,19
                    16,17,18  17,19,18   10,11, 8  11, 9, 8
                     9,12,17  12,20,17   14,11,22  11,19,22
                    12,13,20  13,21,20   13,15,21  15,23,21
                    15,14,23  14,22,23   20,21,22  21,23,22
                    14,15,12  15,13,12   17,20,24  20,25,24
                    19,17,26  17,24,26   20,22,25  22,27,25
                    22,19,27  19,26,27   24,25,26  25,27,26" />

   </GeometryModel3D.Geometry>

If you don't have a modeling program then graph paper is the way to go. Sorry I don't have the original graph paper sketch to go with the Positions above.

User Interface Design

The user interface in design view: screen capture using PtrScr (Print Screen)

Each model needs Material for the surfaces. If you want a solid color it's very simple. If you want to use a bitmap or a gradient, you need to change the brush in the Material to an ImageBrush or a LinearGradientBrush, or whatever type you want.

  <!-- defines the surface of the object -->
     <GeometryModel3D.Material>
         <DiffuseMaterial>
             <DiffuseMaterial.Brush>
                  <SolidColorBrush Color="Gold" />
             </DiffuseMaterial.Brush>
         </DiffuseMaterial>
      </GeometryModel3D.Material>

If you are going to animate your 3DModel in XAML you need to add a Transform for the type of animation (change to a property) that you want to do. If you want to be able to control/change the animation in the C# code-behind file you need to supply a name in the XAML. I wanted to be able to change the axis of rotation from Z to X or Y, so I gave the AxisAngleRotation3D a name using the x: tag so that the C# code could refer to it and change it while the program was running. The Transforms here are triggered when the grid is loaded.

<Grid.Triggers>
   <!-- when the grid has loaded, begin the animation -->
  <EventTrigger RoutedEvent="Grid.Loaded">
<!-- each model has a Transform, Geometry and Material -->        
    
<GeometryModel3D>
    <!-- rotate the geometry about the z-axis - to start -->
         <GeometryModel3D.Transform>
              <RotateTransform3D>
                   <RotateTransform3D.Rotation>
         <!-- naming it lets us access in C# code
              and change the axis of rotation -->
                        <AxisAngleRotation3D x:Name="rotation"
                                Angle="0" Axis="0,0,1" />
              </RotateTransform3D.Rotation>
                       </RotateTransform3D>
         </GeometryModel3D.Transform>

In XAML you need a Storyboard to be able to animate your 3D models. After I had gotten the one cross to work I added 2 more smaller ones on each side. So all three need their own Transform and Storyboard, and of course their own mesh geometry. Again we give the Storyboard a name so we can refer to it in the C# code-behind.

<!-- we have three 3d models, each needs it's own storyboard
     for the animations -->
     <!-- storyboard for large center cross -->
     <BeginStoryboard>
     <!-- naming Storyboard lets us refer to it in C# code-behind -->   
          
<Storyboard Name="centerStoryboard"
               Storyboard.TargetName="rotation"
               RepeatBehavior="Forever">
               <!-- rotate the object 360 degrees -->
               <DoubleAnimation Storyboard.TargetProperty="Angle"
                   To="360" Duration="0:0:3" />
          </Storyboard>
     </BeginStoryboard>

At the bottom of the window I added another Grid, and some nested StackPanels to hold GroupBoxes with RadioButtons to control the axis of rotation for each model, and some command Buttons to Start and Stop the animation, as well as change the camera position to zoom in or out and return to the start position. The animation starts when the Grid is loaded, and for reasons I haven't yet discovered, the Stop animation button will only work after you have pressed the Start button (even though it's already started). I am assuming you know how to use RadioButtons and command Buttons and assign their events like this.

<RadioButton Name="leftXradio" Margin="5"
      Checked="leftXradio_Checked">x-axis</RadioButton> 

<Button Name="zoomInButton" Margin="5" Width="75"
              Click="zoomInButton_Click">Zoom in</Button>

If not you will see it in the XAML code and it's full of comments. The C# code-behind is fairly short and simple. I used some Boolean variables to tell which camera position was being used and some Point3D variables to hold the different camera positions.

// variables to hold camera positions
private Point3D normalPosition;
private Point3D zoomedOutPosition;
private Point3D zoomedInPosition;
// booleans for what view the camera is using
private bool normalView;
private bool zoomedOutView;
private bool zoomedInView;

I set the values for the camera positions, and the events for the RadioButtons and command Buttons change the axis of rotation and camera position. Each cross has a RadioButton to change the the X,Y or Z axis of rotation, and there are 3 buttons to zoom-in, zoom-out and return to the original camera position.

private void setMyValues()
{
normalView = 
true;      // start with normal
      zoomedOutView = false;
      zoomedInView = 
false;
      
// define the camera positions to use
      normalPosition = new Point3D(25, 0, 0);
      zoomedOutPosition = 
new Point3D(40, 0, 0);
      zoomedInPosition = 
new Point3D(10, 0, 0);
// end method setMyValues

I assume you know how to setup RadioButtons and command Buttons and set their events for the C# code-behind file. If not, both the XAML and C# code are commented so you can see what is happening.

// radio buttons for left small cross rotation axis
private void leftXradio_Checked(object sender, RoutedEventArgs e)
{
      leftRotation.Axis = 
new Vector3D(1, 0, 0); // set rotation axis
// end method leftXradio_Checked
private void zoomInButton_Click(object sender, RoutedEventArgs e)
{
      camera.Position = zoomedInPosition;    
// zoom in 15 units
      zoomedOutView = false;
      zoomedInView = 
true;
      normalView = 
false;
// end method zoomOut_Click

The screenshots below show some of the different rotation combinations. Each of the 3DModels can be independently controlled using the RadioButton in their GroupBox for either the X, Y or Z axis for rotation. All three crosses have the origin (0,0,0) as the reference point for their rotations. It is just as easy to specify a different reference point, for example to have the small crosses rotate at their center point. I forgot to do a screenshot of the zoom-in, zoom-out feature. It changes the camera position from the normal view 25 units away to either 10 units (zoom-in) or 40 units (zoom-out). You will be able to see this when you run the program.

Rotating on Z Axis

All 3 crosses rotating on the Z axis. Small crosses "orbit" the large one.

Rotating on X Axis

Small crosses rotating on X axis, large cross on Y axis.

Left and Center Cross

Left and center cross rotating on X axis, right cross on Y axis.

All Rotation

Z, Y and X rotations. Left small cross "orbits" vertically, right cross horizontally.
 
Both the XAML code and the C# code-behind file are commented to explain what is going on. With this example you should be able to understand the basics of Model3DGroups, Animation and changing the camera position in your own 3D environment. It was after this that I started writing a 3D maze game, which is almost done. Still trying to figure out how to animate the camera turning. At the moment the left and right turns “snap” to the new direction. But, I will figure it out. I have yet to find a problem I can't solve.

Good luck and have fun with 3D and XAML/C#.

Garry