13,347,998 members (55,272 online)
alternative version

#### Stats

26.4K views
18 bookmarked
Posted 5 Nov 2010

# Walking Robot Series In WPF -- Part 2: Circles, Cylinders, Flat And Smooth Shading

, 5 Nov 2010
 Rate this:
Part two of a series on how to make an animated 3D robot in WPF using C# code

## Introduction

This is part 2 in the walking robot series where we create a simple animated robot character using C# code in WPF.

Let's use the WpfCube class that we defined in the last article to create a simple environment:

double floorThickness = WpfScene.sceneSize / 100;
GeometryModel3D floorModel = WpfCube.CreateCubeModel(
new Point3D(-WpfScene.sceneSize / 2,
-floorThickness,
-WpfScene.sceneSize / 2),
WpfScene.sceneSize, floorThickness, WpfScene.sceneSize, Colors.Tan);

Model3DGroup groupScene = new Model3DGroup();

We call this code upon loading our Viewport3D using the event we defined before. This will create a floor for our robot to walk upon. For this article, we are just going to define some more of the shapes we need to make our robot.

We will need a circle class. Here is some of the code we need to construct a circle:

private int nSides = 6;

private Point3D center;

private List<Point3D> points;

public WpfCircle(int NSides, Point3D Center, double Radius)
{
nSides = NSides;

angle = (double)360.0 / (double)nSides;

center = new Point3D(Center.X, Center.Y, Center.Z);

makeCircle();
}

{
nSides = NSides;

angle = (double)360.0 / (double)nSides;

center = new Point3D(Center.X, Center.Y, Center.Z);

makeCircle();
}

private void makeCircle()
{
points = new List<Point3D>();

top = new Point3D(center.X, center.Y + radiusY, center.Z);

for (int i = 1; i < nSides; i++)
{
Point3D p = WpfUtils.RotatePointXY

{
double diff = p.X - center.X;
p = new Point3D(center.X + diff, p.Y, p.Z);
}

}
}

Only a center point and X and Y radius are needed to construct our circle. As before, we need a method to add the circle's triangles to a geometry mesh:

public void addToMesh(MeshGeometry3D mesh, bool combineVertices)
{
if (points.Count > 2)
{
List<Point3D> temp = new List<Point3D>();

foreach (Point3D p in points)
{
}

for (int i = 1; i < temp.Count; i++)
{
mesh, combineVertices);
}
}
}

We provide methods to create a GeometryModel3D from a circle object. To illustrate a point about how WPF works, here are two versions of this method:

public GeometryModel3D createModel(Color color, bool combineVertices)
{
MeshGeometry3D mesh = new MeshGeometry3D();

Material material = new DiffuseMaterial(
new SolidColorBrush(color));

GeometryModel3D model = new GeometryModel3D(mesh, material);

return model;
}

public GeometryModel3D createModelTwoSided(Color color, bool combineVertices)
{
MeshGeometry3D mesh = new MeshGeometry3D();

Material material = new DiffuseMaterial(
new SolidColorBrush(color));

GeometryModel3D model = new GeometryModel3D(mesh, material);

model.BackMaterial = material;

return model;
}

One version of the createModel method makes a two-sided model. The only difference is this line of code:

model.BackMaterial = material;

If you run the demo, you will see the two circles that are created spinning around. One of the circles is visible from both sides because it has a BackMaterial set. The other one is only available from one side.

We are not going to use any circles for our robot, but we need the circle so that we can construct cylinder shapes that we will be using for the robot. Here are the beginnings of our cylinder class:

private WpfCircle front;

private WpfCircle back;

private int nSides;

private double length;

private Point3D center;

double Length)
{
center = Center;
nSides = NSides;
length = Length;

front = new WpfCircle(nSides, center, frontRadius);

backcenter = new Point3D(center.X, center.Y, center.Z - length);

back = new WpfCircle(nSides, backcenter, backRadius);
}

As you can see, we use two circles to construct our cylinder. Here is the method that adds the triangles to the mesh for this shape. It adds the triangles for the sides, then lets each circle object add its own triangles to the mesh:

public void addToMesh(MeshGeometry3D mesh, bool encloseTop, bool combineVertices)
{
if (front.getPoints().Count > 2)
{
List<Point3D> frontPoints = new List<Point3D>();
foreach (Point3D p in front.getPoints())
{
}

List<Point3D> backPoints = new List<Point3D>();
foreach (Point3D p in back.getPoints())
{
}

for (int i = 1; i < frontPoints.Count; i++)
{
backPoints[i - 1], frontPoints[i], mesh, combineVertices);
backPoints[i - 1], backPoints[i], mesh, combineVertices);
}

if (encloseTop)
{
}
}
}

public static void addPointCombined(Point3D point, MeshGeometry3D mesh, Vector3D normal)
{
bool found = false;

int i = 0;

foreach (Point3D p in mesh.Positions)
{
if (p.Equals(point))
{
found = true;
break;
}

i++;
}

if (!found)
{
}
}

public static void addTriangleToMesh(Point3D p0, Point3D p1, Point3D p2,
MeshGeometry3D mesh, bool combine_vertices)
{
Vector3D normal = CalculateNormal(p0, p1, p2);

if (combine_vertices)
{
}
else
{
}
}

You can see the effect this has by running the demo. In addition to the two spinning circles, we have two spinning cylinders that we add to our model. One has flat shading and the other one has smooth shading, so you can see the difference. Here is the cylinder creation code we use in our Loaded event for our Viewport3D back in the MainWindow.xaml.cs file:

Cylinder cylinder = new Cylinder(
new Point3D(0, WpfScene.sceneSize / 4, WpfScene.sceneSize / 5),
40, WpfScene.sceneSize / 8,
WpfScene.sceneSize / 8,
WpfScene.sceneSize / 6);

Cylinder cylinder2 = new Cylinder(
new Point3D(-WpfScene.sceneSize / 2, WpfScene.sceneSize / 4,  0),
40, WpfScene.sceneSize / 8,
WpfScene.sceneSize / 8,
WpfScene.sceneSize / 6);

GeometryModel3D cylinderModel = cylinder.CreateModel(Colors.AliceBlue, true, true);

GeometryModel3D cylinderModel2 = cylinder2.CreateModel(Colors.AliceBlue, true, false);

Model3DGroup groupScene = new Model3DGroup();

We use the same method as before to set our circles and cylinders spinning:

public void turnModel(Point3D center, GeometryModel3D model,
double beginAngle, double endAngle, double seconds, bool forever)
{
Vector3D vector = new Vector3D(0, 1, 0);

AxisAngleRotation3D rotation = new AxisAngleRotation3D(vector, 0.0);

DoubleAnimation doubleAnimation = new DoubleAnimation
(beginAngle, endAngle, durationTS(seconds));

if (forever)
{
doubleAnimation.RepeatBehavior = RepeatBehavior.Forever;
}

doubleAnimation.BeginTime = durationTS(0.0);

RotateTransform3D rotateTransform = new RotateTransform3D(rotation, center);

model.Transform = rotateTransform;

rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, doubleAnimation);
}

So here is the entire Loaded event for our viewport, where we create our scene:

private void Viewport3D_Loaded(object sender, RoutedEventArgs e)
{
if (sender is Viewport3D)
{
Viewport3D viewport = (Viewport3D)sender;

Cylinder cylinder = new Cylinder(
new Point3D(0, WpfScene.sceneSize / 4, WpfScene.sceneSize / 5),
40, WpfScene.sceneSize / 8,
WpfScene.sceneSize / 8,
WpfScene.sceneSize / 6);

Cylinder cylinder2 = new Cylinder(
new Point3D(-WpfScene.sceneSize / 2, WpfScene.sceneSize / 4,  0),
40, WpfScene.sceneSize / 8,
WpfScene.sceneSize / 8,
WpfScene.sceneSize / 6);

WpfCircle circle = new WpfCircle(55,
new Point3D(-WpfScene.sceneSize / 2, WpfScene.sceneSize / 6,
WpfScene.sceneSize / 2), WpfScene.sceneSize / 15);

WpfCircle circle2 = new WpfCircle(55,
new Point3D(0, WpfScene.sceneSize / 6, WpfScene.sceneSize / 2),
WpfScene.sceneSize / 15);

GeometryModel3D cylinderModel =
cylinder.CreateModel(Colors.AliceBlue, true, true);

GeometryModel3D cylinderModel2 =
cylinder2.CreateModel(Colors.AliceBlue, true, false);

GeometryModel3D circleModel = circle.createModel(Colors.Aqua, false);
GeometryModel3D circleModel2 = circle2.createModelTwoSided(Colors.Aqua, false);

double floorThickness = WpfScene.sceneSize / 100;
GeometryModel3D floorModel = WpfCube.CreateCubeModel(
new Point3D(-WpfScene.sceneSize / 2,
-floorThickness,
-WpfScene.sceneSize / 2),
WpfScene.sceneSize, floorThickness, WpfScene.sceneSize, Colors.Tan);

Model3DGroup groupScene = new Model3DGroup();

viewport.Camera = camera();

ModelVisual3D visual = new ModelVisual3D();

visual.Content = groupScene;

turnModel(cylinder.getCenter(), cylinderModel, 0, 360, 13, true);
turnModel(cylinder2.getCenter(), cylinderModel2, 0, 360, 13, true);

turnModel(circle.getCenter(), circleModel, 0, 360, 8, true);
turnModel(circle2.getCenter(), circleModel2, 0, 360, 8, true);
}
}

In this article, we added a couple more shapes that we need for our robot. We can now make triangles, rectangles, cubes, circles, and cylinders. We also demonstrated how to make two-sided models in WPF by using BackMaterial and how to get smooth shading as opposed to flat shading by combining vertices.

In our next article, we will add the final shape classes we need for our robot, then we will show how to use animated transforms and storyboards to set the whole thing moving and bring the robot to life.

## History

• 4th November, 2010: Initial version

## Share

 Software Developer United States
Gary Miller is a professional software developer whose clients have included Delta Air Lines, Cingular Wireless, and the Center For Disease Control. He has developed advanced graphical user interfaces for traffic control systems and medical call centers. Some animated 3D products he has developed include Field Artist and Calder4D. He has developed a Silverlight powered social networking site for music students called Field Artist Central

Calder4D is a Windows program that allows you to model and animate 3D shapes and then produces the XAML code to include those animated shapes in your own WPF applications.

## You may also be interested in...

 First Prev Next
 My vote of 5 Anurag Gandhi9-Nov-10 1:09 Anurag Gandhi 9-Nov-10 1:09
 Screenshots? torial5-Nov-10 6:48 torial 5-Nov-10 6:48
 Re: Screenshots? Gary.Miller.WPF5-Nov-10 8:44 Gary.Miller.WPF 5-Nov-10 8:44
 Last Visit: 31-Dec-99 19:00     Last Update: 18-Jan-18 4:50 Refresh 1