Now, I will describe how to develop a Cover Flow component in WPF.

The first basic task will just display one cover in the middle of the screen.
In 3D, we usually work with triangles.
So we simply cut the square in two :
Let's start with the code. We create the 4 points :
- var p0 = new Point3D(-1, -1, 0);
- var p1 = new Point3D(1, -1, 0);
- var p2 = new Point3D(1, 1, 0);
- var p3 = new Point3D(-1, 1, 0);
- private Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
- {
- var v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
- var v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
- return Vector3D.CrossProduct(v0, v1);
- }
- var mesh = new MeshGeometry3D();
- mesh.Positions.Add(p0);
- mesh.Positions.Add(p1);
- mesh.Positions.Add(p2);
- mesh.Positions.Add(p3);
- var normal = CalculateNormal(p0, p1, p2);
- mesh.TriangleIndices.Add(0);
- mesh.TriangleIndices.Add(1);
- mesh.TriangleIndices.Add(2);
- mesh.Normals.Add(normal);
- normal = CalculateNormal(p2, p3, p0);
- mesh.TriangleIndices.Add(2);
- mesh.TriangleIndices.Add(3);
- mesh.TriangleIndices.Add(0);
- mesh.Normals.Add(normal);
- var q0 = new Point(0, 0);
- var q1 = new Point(1, 0);
- var q2 = new Point(1, 1);
- var q3 = new Point(0, 1);


But 2D texturing uses :
So the coordinates associations are :
- p0 <-> q3
- p1 <-> q2
- p2 <-> q1
- p3 <-> q0
- mesh.TextureCoordinates.Add(q3);
- mesh.TextureCoordinates.Add(q2);
- mesh.TextureCoordinates.Add(q1);
- mesh.TextureCoordinates.Add(q0);
- mesh.TextureCoordinates.Add(q1);
- mesh.TextureCoordinates.Add(q2);
This gives us a Tesselate method that will return the mesh :
- private Geometry3D Tessellate()
- {
- var p0 = new Point3D(-1, -1, 0);
- ...
- var mesh = new MeshGeometry3D();
- ...
- mesh.Freeze();
- return mesh;
- }
- private ImageSource LoadImageSource(string imagePath)
- {
- Image thumb = Image.FromFile(imagePath);
- return new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute));
- }
- private Material LoadImage(ImageSource imSrc)
- {
- return new DiffuseMaterial(new ImageBrush(imSrc));
- }
- using ...
- namespace Ded.Tutorial.Wpf.CoverFlow.Part1
- {
- class Cover : ModelVisual3D
- {
- #region Fields
- private readonly Model3DGroup modelGroup;
- #endregion
- #region Private stuff
- private Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)...
- private Geometry3D Tessellate()...
- private ImageSource LoadImageSource(string imagePath)...
- private Material LoadImage(ImageSource imSrc)...
- #endregion
- public Cover(string imagePath)
- {
- ImageSource imSrc = LoadImageSource(imagePath);
- modelGroup = new Model3DGroup();
- modelGroup.Children.Add(new GeometryModel3D(Tessellate(), LoadImage(imSrc)));
- Content = modelGroup;
- }
- }
- }

We place the camera at (0, 0, 3). We add a light source so that we will not see black objects.
We also add an empty ModelVisual3D object that will contain our single cover (for now).
Here is the xaml code :
- <Window x:Class="Ded.Tutorial.Wpf.CoverFlow.Part1.TestWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="WPF Coverflow" Height="320" Width="512">
- <Grid>
- <Viewport3D x:Name="viewPort" Grid.Column="0" Grid.Row="0" ClipToBounds="False">
- <Viewport3D.Camera>
- <PerspectiveCamera x:Name="camera" Position="0,0,3"
- UpDirection="0,1,0" LookDirection="0,0,-1"
- FieldOfView="100" NearPlaneDistance="0.125"/>
- </Viewport3D.Camera>
- <Viewport3D.Children>
- <ModelVisual3D>
- <ModelVisual3D.Content>
- <DirectionalLight Color="White" Direction="0,0,-4" />
- </ModelVisual3D.Content>
- </ModelVisual3D>
- <ModelVisual3D x:Name="visualModel">
- </ModelVisual3D>
- </Viewport3D.Children>
- </Viewport3D>
- </Grid>
- </Window>

- <Grid.Background>
- <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
- <LinearGradientBrush.GradientStops>
- <GradientStop Color="Black" Offset="0"/>
- <GradientStop Color="#696988" Offset="1"/>
- </LinearGradientBrush.GradientStops>
- </LinearGradientBrush>
- </Grid.Background>

Edit 2011.06.07 : Only one normal is needed per triangle. Source code has not been updated.