In this post we will use Point3DAnimationUsingPath from the previous post to rotate the viewpoint around a 3D cube while playing a video on all six of its faces.
You can download the complete source, with the Point3DAnimationUsingPath class from here.

"Look at me!
Look at me now!" said the cat.
"With a cup and a cake
On the top of my hat!
I can hold up TWO books!
I can hold up the fish!
And a litte toy ship!
And some milk on a dish!
And look!
I can hop up and down on the ball!
But that is not all!
Oh, no.
That is not all... “, Dr.Seuss
Motivation
It is fairly straightforward to rotate 3D objects in WPF using the various Transform3D derived classes. But sometimes what you need is to rotate the viewer around the scene and not to rotate individual objects. That is, you need to animate the Position property of the Camera object over a path in three dimensions.
The problem is that the type of the Position property is Point3D and WPF doesnt provide a Point3DAnimationUsingPath class. I partially filled that gap with a class of that name in this previous post.
In addition, as we modify the Position of the Camera, we need to change the LookDirection so that the Camera always points to the same point in space.
To demonstrate the solution, I will place a cube at the center of the 3D coordinate system and play video on each side so you can clearly see how we are rotating around the object.
“Look at me! Look at me now!”, say I, “With a cube in the center and a video on SIX faces, I can rotate around it and look back to the center! But that is not all! Oh no, that is not all…”
Solution
I will build this solution in 8 steps:
- Describe the cube.
- Create a MediaElement that plays video continuously.
- Map the MediaElement on to each of the six faces of the cube.
- Define the light source.
- Define the camera and bind the LookDirection to a function of its Position.
- Describe the path over which we will animate the Position.
- Apply the Point3DAnimationUsingPath to the Position property of the Camera
- Put it all together.
Describe the Cube
We describe the cube as a MeshGeometry3D and place it in a GeometryModel3D. This can then be placed inside a ModelVisual3D which will be added to the Viewport3D.
In the MeshGeometry3D we define the Positions and TriangleIndices as follows:
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions=
"-1,-1,-1 1,-1,-1 1, 1,-1 -1, 1,-1
-1,-1,-1 -1, 1,-1 -1, 1, 1 -1,-1, 1
1,-1,-1 -1,-1,-1 -1,-1, 1 1,-1, 1
1, 1,-1 1,-1,-1 1,-1, 1 1, 1, 1
1,-1, 1 -1,-1, 1 -1, 1, 1 1, 1, 1
-1, 1,-1 1, 1,-1 1, 1, 1 -1, 1, 1"
TriangleIndices=
"3,2,1 1,0,3
7,6,5 5,4,7
11,10,9 9,8,11
15,14,13 13,12,15
19,18,17 17,16,19
23,22,21 21,20,23"
</MeshGeometry3D>
</GeometryModel3D.Geometry>
There are more than one ways to do this, but I chose the more verbose way in order to make it easier to define the mapping between the MediaElement and the faces of the cube.
In the Positions collection there are six lines, one for each face of the cube, four points for each face.
In the TriangleIndices there are six lines, one for each face of the cube, two triangles for each face. Note that the order of the points in the triangles is counterclockwise around the outfacing norm to the triangle’s plane.
Here is a diagram of the cube and its vertices:
Create the MediaElement
The MediaElement itself is not new, but this XAML snippet also shows how it is used as the Visual property of a VisualBrush that we will use to paint the Material of our GeometryModel3D.
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<VisualBrush>
<VisualBrush.Visual>
<MediaElement>
<MediaElement.Triggers>
<EventTrigger RoutedEvent="MediaElement.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<MediaTimeline x:Name="mediaTimeline"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</MediaElement.Triggers>
</MediaElement>
</VisualBrush.Visual>
</VisualBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
This MediaElement has a MediaTimeline that plays forever. There is something missing, of course - the Uri identifying the source of the media. But, no fear, we will set that in code. That’s why the MediaTimeline has been given a name.
Map the MediaElement on to the Cube
There are two parts to this mapping.
The first part, completed in the previous step, is to set the Visual property of the VisualBrush for the Material. The second part is to provide a relative coordinate in the 2D space of the VisualBrush for each 3D coordinate in the Positions collection. I defined the Positions collection in a consistent way for each face, so it turns out that the texture mapping is identical for each of the six faces.
We will add this XAML snippet to the properties of the MeshGeometry3D (after TriangleIndices, for example):
TextureCoordinates=
"1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0">
Define the Light Source
This one is simple. We will use an ambient white light to make sure that there is no shading on the cube.
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"></AmbientLight >
</ModelVisual3D.Content>
</ModelVisual3D>
Define the Camera
We will use a PerspectiveCamera in order to achieve the illusion of depth.
<Viewport3D.Camera>
<PerspectiveCamera
x:Name="camera"
UpDirection="0,0,1"
LookDirection="{
Binding RelativeSource={RelativeSource Self},
Path=Position,
Converter={StaticResource lookBackConverter}}"
Position="0,0,0" />
</Viewport3D.Camera>
The value of the Position property is meaningless because we are going to control this property with an animation. But, the LookDirection is interesting. In order to move the Camera and still look at the cube, we need to modify the look direction as the Position changes. This is how:
Camera.LookDirection = CenterPointOfInterest – Camera.Position
We could choose to animate the LookDirection concurrently with the Position property, but I chose a simpler, and in my opinion, a more elegant way.
I defined a converter from Camera.Position to Camera.LookDirection and apply it to a binding of LookDirection to Position.
This is the code for the converter:
class LookBackConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(
object value, Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
return new Point3D(0,0,0) - (Point3D)value;
}
public object ConvertBack(
object value, Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
return null;
}
#endregion
}
We could expose the center of interest as a public property of the LookBackConverter and set it in XAML, but for this demo, I will just assume its the origin of the coordinate system.
Describe the Path over which we will Animate the Position
You will recall from the previous post that Point3DAnimationUsingPath is lacking in that it doesn’t accept a description of a path in 3D space, but rather a constant Z coordinate and a path in 2D space.
So, we need to define a circle around the Z axis, with a diameter larger than the side of the cube, but not too much larger. The length of each side of the cube is 2 units, so 4 will do.
Unfortunately there is no EllipseGeometry for 2D, so I created my own with 2 ArcSegments. Here is the XAML:
<PathGeometry x:Key="circlePath">
<PathGeometry.Figures>
<PathFigure StartPoint="-4, -4" IsClosed="False">
<ArcSegment Point="4,4" Size="4, 4" />
<ArcSegment Point="-4,-4" Size="4,4" />
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
Apply the Point3DAnimationUsingPath to the Camera Position
Armed with the Point3DAnimationUsingPath and the circlePath PathGeometry it is now easy to define the animation we need:
<Viewport3D.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<local:Point3DAnimationUsingPath
Storyboard.TargetName="camera"
Storyboard.TargetProperty="Position"
Duration="0:0:20"
Z="2"
PathGeometry="{StaticResource circlePath}"
RepeatBehavior="Forever">
</local:Point3DAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Viewport3D.Triggers>
As you can see, the camera named “camera” is the animation target and Position is the name of the target property. We set the circlePath PathGeometry as the PathGeometry and Z=2 so we rotate around the cube, but above it.
Putting It All Together
Here is the entire XAML for the solution:
<Window x:Class="RotatingCamera.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RotatingCamera"
Title="Window1"
Width="500"
Height="400"
Loaded="Window_Loaded">
<Grid>
<Viewport3D>
<Viewport3D.Resources>
<local:LookBackConverter x:Key="lookBackConverter" />
<PathGeometry x:Key="circlePath">
<PathGeometry.Figures>
<PathFigure StartPoint="-4, -4" IsClosed="False">
<ArcSegment Point="4,4" Size="4, 4" />
<ArcSegment Point="-4,-4" Size="4,4" />
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</Viewport3D.Resources>
<Viewport3D.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<local:Point3DAnimationUsingPath
Storyboard.TargetName="camera"
Storyboard.TargetProperty="Position"
Duration="0:0:20"
Z="2"
PathGeometry="{StaticResource circlePath}"
RepeatBehavior="Forever">
</local:Point3DAnimationUsingPath>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Viewport3D.Triggers>
<Viewport3D.Camera>
<PerspectiveCamera
x:Name="camera"
UpDirection="0,0,1"
LookDirection="{
Binding RelativeSource={RelativeSource Self},
Path=Position,
Converter={StaticResource lookBackConverter}}"
Position="0,0,0" />
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<AmbientLight Color="White"></AmbientLight >
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions=
"-1,-1,-1 1,-1,-1 1, 1,-1 -1, 1,-1
-1,-1,-1 -1, 1,-1 -1, 1, 1 -1,-1, 1
1,-1,-1 -1,-1,-1 -1,-1, 1 1,-1, 1
1, 1,-1 1,-1,-1 1,-1, 1 1, 1, 1
1,-1, 1 -1,-1, 1 -1, 1, 1 1, 1, 1
-1, 1,-1 1, 1,-1 1, 1, 1 -1, 1, 1"
TriangleIndices=
"3,2,1 1,0,3
7,6,5 5,4,7
11,10,9 9,8,11
15,14,13 13,12,15
19,18,17 17,16,19
23,22,21 21,20,23"
TextureCoordinates=
"1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0
1,1 0,1 0,0 1,0">
</MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<VisualBrush>
<VisualBrush.Visual>
<MediaElement>
<MediaElement.Triggers>
<EventTrigger RoutedEvent="MediaElement.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<MediaTimeline x:Name="mediaTimeline"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</MediaElement.Triggers>
</MediaElement>
</VisualBrush.Visual>
</VisualBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
</Grid>
</Window>
and this is what my cube looked like in one of my orbits around it.
Summary
In this post I demonstrated the use of my Point3DAnimationUsingPath to animate the Position of a Camera around a 3D object.
Its amazing how with WPF you can have so much going on in parallel, video, animations and databinding. Like the Cat in The Hat, really.