In this post, I explain the basic Windows Presentation Foundation programming elements of a 3D scene in C#. The C# code that I use for demonstration creates a simple tetrahedron model and rotates it around the vertical axis.
To create the project, follow these steps:
- Begin with the project from our prior blog post, or create a new WPF project.
- Add a class file to the project: left-click PROJECT in the menubar and left-click Add Class… in the submenu.
- This pops up the Add New Item dialog. Select Installed->Visual C# Items in the left-hand pane.
- Then left-click Class in the center pane to select it.
- Finally, rename the class by left-clicking the text box next to Name: at the bottom of the window and entering CScene3d.cs. Finish adding the class by left-clicking the Add button.
- Next, copy the code for “CScene3D.cs” below into the file of the same name in your project.
- Finally, finish the code by adding this line to “Program.cs” as shown in the code below to allow the window to display the 3D scene:
qWindow.Content = TestScenes.Test5();
- Compile and execute the program by left-clicking DEBUG in the menubar and left-clicking Start Without Debugging in the submenu. After a few seconds, the code will be compiled and a window will pop up displaying a rotating tetrahedron.
The WPF scene creation in “CScene3D.cs” consists of a few basic steps, which can be outlined as follows:
- Create a camera and add it.
- Create a lighting model and add it.
- Create a geometric model and add it
- Create points, triangles, and normals
- Set the material properties
- Create and apply transforms
For simplicity, I have skipped some steps, like adding normals, and accepted default values for much of the rest. At the end, I have collected components to added them appropriately. Pay close attention to that.
Program.cs
using System;
using System.Windows;
namespace ConsoleApplication {
class Program {
[STAThread]
static void Main(string[] args) {
Window qWindow = new Window();
qWindow.Title = "WPF in Console";
qWindow.Width = 400;
qWindow.Height = 300;
qWindow.Content = CScene3D.Test();
qWindow.ShowDialog();
}
}
}
CScene3D.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
namespace ConsoleApplication {
class CScene3D {
// Animation - Tetrahedron (upright, looking slightly up from below)
public static Viewport3D Test() {
// Define the camera
PerspectiveCamera myPCamera = new PerspectiveCamera();
myPCamera.Position = new Point3D(0, .2, 3);
// Define a lighting model
DirectionalLight myDirectionalLight = new DirectionalLight();
// Define the geometry
const double kdSqrt2 = 1.4142135623730950488016887242097;
const double kdSqrt6 = 2.4494897427831780981972840747059;
// Create a collection of vertex positions
Point3DCollection myPositionCollection = new Point3DCollection();
myPositionCollection.Add(new Point3D(0.0, 1.0, 0.0));
myPositionCollection.Add(new Point3D(2.0 * kdSqrt2 / 3.0, -1.0 / 3.0, 0.0));
myPositionCollection.Add(new Point3D(-kdSqrt2 / 3.0, -1.0 / 3.0, kdSqrt6 / 3.0));
myPositionCollection.Add(new Point3D(-kdSqrt2 / 3.0, -1.0 / 3.0, -kdSqrt6 / 3.0));
// Create a collection of triangle indices
Int32Collection myTriangleIndicesCollection = new Int32Collection();
// Triangle
myTriangleIndicesCollection.Add(0);
myTriangleIndicesCollection.Add(2);
myTriangleIndicesCollection.Add(1);
// Triangle
myTriangleIndicesCollection.Add(0);
myTriangleIndicesCollection.Add(1);
myTriangleIndicesCollection.Add(3);
// Triangle
myTriangleIndicesCollection.Add(0);
myTriangleIndicesCollection.Add(3);
myTriangleIndicesCollection.Add(2);
// Triangle
myTriangleIndicesCollection.Add(1);
myTriangleIndicesCollection.Add(2);
myTriangleIndicesCollection.Add(3);
MeshGeometry3D myMeshGeometry3D = new MeshGeometry3D();
myMeshGeometry3D.Positions = myPositionCollection;
myMeshGeometry3D.TriangleIndices = myTriangleIndicesCollection;
// Apply the mesh to the geometry model.
GeometryModel3D myGeometryModel = new GeometryModel3D();
myGeometryModel.Geometry = myMeshGeometry3D;
// Define the material for the geometry
SolidColorBrush qColorBrush = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));
DiffuseMaterial myMaterial = new DiffuseMaterial(qColorBrush);
myGeometryModel.Material = myMaterial;
// Define the transformation, if any. In this case, we use an animated transformation
RotateTransform3D myRotateTransform =
new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 1));
DoubleAnimation myAnimation = new DoubleAnimation();
myAnimation.From = 1;
myAnimation.To = 361;
myAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(5000));
myAnimation.RepeatBehavior = RepeatBehavior.Forever;
myRotateTransform.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myAnimation);
myGeometryModel.Transform = myRotateTransform;
// Collect the components
Model3DGroup myModel3DGroup = new Model3DGroup();
myModel3DGroup.Children.Add(myDirectionalLight);
myModel3DGroup.Children.Add(myGeometryModel);
ModelVisual3D myModelVisual3D = new ModelVisual3D();
myModelVisual3D.Content = myModel3DGroup;
Viewport3D myViewport3D = new Viewport3D();
myViewport3D.Children.Add(myModelVisual3D);
myViewport3D.Camera = myPCamera;
return myViewport3D;
}
}
}