Blog'A'Little

Just another C# guy

Spinning cube in Silverlight Alpha 1.1 using C#

Silverlight represents a subset of WPF and it's features, one of the features that aren't part of the subset is 3D. When the Silverlight 1.1 Alpha with the support for managed code came out earlier this year, I couldn't wait to get my hands dirty and try to get some 3D content on it even though it didn't support it. I got very happy when I saw they left the Polygon visual in there, that means you have some optimized way of drawing triangles. Anywho, the purpose of this tutorial is to show how little code you can get away with if you just want to have a simple spinning cube.

The sample is very basic and does not involve any heavy geometry math. If you want a sample using Matrix math and more advanced geomtry stuff, take a look at the Balder project : http://www.codeplex.com/Balder

The main loop

The first thing we need before we do anything else is a main loop. This can be achieved by using the animation system in Silverlight. By creating a storyboard and hook up the Completed event we can achieve a steady callback. In our Page.xaml we create our own Canvas for the spinning cube and add a storyboard inside it :

 
1
2
3
4
5
  <Canvas x:Name="spinningCubeCanvas">
<Canvas.Resources>
<Storyboard x:Name="spinningCubeStorybard"
Completed="spinningCubeCanvas_Render"/>
</Canvas.Resources>
</Canvas>
 

That's in fact all the XAML we will be needing to achieve a spinning cube. Now for the code-behind.
In the Page_Loaded event we add the following code to start our mainloop.

this.spinningCubeStorybard.Begin();
Then we need to implement the Completed event for the Storyboard : 
1
2
3
4
public void spinningCubeCanvas_Render(object sender, EventArgs e)
{
this.spinningCubeStorybard.Begin();
}

 

Our Cube

To define our cube we need points in 3D space, these are called Vertices (one vertex, several vertices). We therefor create a simple class to represent a vertex. The class represents the original vertex before we have done any calculations on it and also contains the finished calculated vertex ready to be used on our 2D screen.

1
2
3
4
5
6
    private class Vertex
    {
      public  double X, Y, Z;
      public double RotatedX, RotatedY, RotatedZ;
      public int TranslatedX, TranslatedY;
    }
 
As you can see, the vertex contains an X,Y and Z representing the coordinate in 3D space. In addition it contains the rotated version and the translated 
(2D) version. Now that we have the definition of a vertex, we need the definition of a triangle that hooks itself on 3 vertices, we call these Faces. A face
contains 3 integers representing an
index into the array of vertices for the object we are rotating.
1
2
3
4
    private class Face
    {
      public int VertexA, VertexB, VertexC;
    }

Now we need to create the array of vertices for the object :

1
2
3
4
5
6
7
8
9
10
    private Vertex[] _vertices = new Vertex[] {
                     new Vertex() { X=-150, Y=-150, Z=-150},
                     new Vertex() { X=150, Y=-150, Z=-150},
                     new Vertex() { X=-150, Y=150, Z=-150},
                     new Vertex() { X=150, Y=150, Z=-150},
                     new Vertex() { X=-150, Y=-150, Z=150},
                     new Vertex() { X=150, Y=-150, Z=150},
                     new Vertex() { X=-150, Y=150, Z=150},
                     new Vertex() { X=150, Y=150, Z=150},
                   };

And we need an array of faces that hooks up to these vertices :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    private Face[] _faces = new Face[] {
                    new Face() { VertexA=2, VertexB=1, VertexC=0},
                    new Face() { VertexA=1, VertexB=2, VertexC=3},
                    new Face() { VertexA=4, VertexB=5, VertexC=6},
                    new Face() { VertexA=7, VertexB=6, VertexC=5},
                    new Face() { VertexA=0, VertexB=4, VertexC=6},
                    new Face() { VertexA=0, VertexB=6, VertexC=2},
                    new Face() { VertexA=7, VertexB=5, VertexC=1},
                    new Face() { VertexA=3, VertexB=7, VertexC=1},
                    new Face() { VertexA=5, VertexB=4, VertexC=0},
                    new Face() { VertexA=1, VertexB=5, VertexC=0},
                    new Face() { VertexA=2, VertexB=6, VertexC=7},
                    new Face() { VertexA=2, VertexB=7, VertexC=3},
                };
 
 
The "Magic"
Now we are good to go to implement all the rendering to make this into a spinning cube. First we need to rotate all the vertices around the 
X,Y and Z axis. We do this very simple, no matrix math involved, very very basic geometry stuff involving sin and cos.
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
      // Calculate all the vertices
      foreach (Vertex vertex in this._vertices)
      {
        // Rotate the vertex around the Z axis
        tempY1 = (vertex.X * Math.Sin(this._zRotation)) +
(vertex.Y * Math.Cos(this._zRotation));
        tempX1 = (vertex.X * Math.Cos(this._zRotation)) -
(vertex.Y * Math.Sin(this._zRotation));

        // Rotate the vertex around the Y axis
        vertex.RotatedX = (vertex.Z * Math.Sin(this._yRotation)) +
(tempX1 * Math.Cos(this._yRotation));
        tempZ1 = (vertex.Z * Math.Cos(this._yRotation)) -
(tempX1 * Math.Sin(this._yRotation));

        // Rotate the vertex around the X axis
        vertex.RotatedZ = (tempY1 * Math.Sin(this._xRotation)) +
(tempZ1 * Math.Cos(this._xRotation));
        vertex.RotatedY = (tempY1 * Math.Cos(this._xRotation)) -
(tempZ1 * Math.Sin(this._xRotation));

        // Translate the vertex into a 2D coordinate
        vertex.TranslatedX = ((int) ((vertex.RotatedX * focalLength) /
(vertex.RotatedZ + zoom)))+xoffset;
        vertex.TranslatedY = ((int) ((vertex.RotatedY * focalLength) /
(vertex.RotatedZ + zoom)))+yoffset;
      }
 
This gives us all the vertices rotated and translated. Great, we can finally get our cube up and running. The next part gets the vertices based upon the 
index of the vertices in each face and creates a Polygon
visual that we add to the rendering pipeline of Silverlight, in our case we add it to the Canvas
we have
created for our spinning cube. Before we add anything to it we clear it. The loop also does a hidden surface removal, we do not need to render
polygons that aren't really visible.
We want to know which polygons are facing away, this involves doing a mixed product of the 3 vertices.
The result of the mixed product can also be used for our purpose to give the polygon a color, you'll see some strange color magic in the loop. :) 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Create polygons for Silverlight to work with from the newly 
// calculated vertices
this.spinningCubeCanvas.Children.Clear();
foreach (Face face in this._faces)
{
  Vertex vertexA = this._vertices[face.VertexA];
  Vertex vertexB = this._vertices[face.VertexB];
  Vertex vertexC = this._vertices[face.VertexC];

  // Do a mixedproduct of all vertices for hidden surface removal
  double mixedProduct = (vertexB.TranslatedX - vertexA.TranslatedX) *
             (vertexC.TranslatedY - vertexA.TranslatedY) -
             (vertexC.TranslatedX - vertexA.TranslatedX) *
             (vertexB.TranslatedY - vertexA.TranslatedY);
  bool visible = mixedProduct < 0;
  if (!visible)
  {
    continue;
  }


  // Use the mixed product for "shading".
  // The larger the face, the brighter it is.
  double shade = -mixedProduct; // *512;
  shade /= 1024;

  int color = (int)shade;
  color += 128;
  if (color >= 250)
  {
    color = 250;
  }
  if (color < 30)
  {
    color = 30;
  }

  byte red = (byte)(color >> 3);
  byte green = (byte)(color >> 1);
  byte blue = (byte)(color);


  // Create the polygon and initialize the point and the color
  Polygon polygon = new Polygon();
  polygon.Points = new Point[] {
new Point(vertexA.TranslatedX,vertexA.TranslatedY),
new Point(vertexB.TranslatedX,vertexB.TranslatedY),
new Point(vertexC.TranslatedX,vertexC.TranslatedY)
};
  polygon.Fill = new SolidColorBrush(Color.FromRgb(red, green, blue));

  this.spinningCubeCanvas.Children.Add(polygon);
}
 
That's pretty much it...  Now you just need to rotate it. :) 
image 
Download the sourcecode attached to this post for a working version.
 
Update 12th of August 2007 : 
I've updated the attachment to work with Siliverlight 1.1 Alpha Refresh
kick it on DotNetKicks.com kick it on GameDevKicks.com

Comments

Anthony Main said:

I've just downloaded the 8745 build of Balder, but it appears there is an XSD missing ImageAnimation.xsd

kick it on DotNetKicks.com kick it on GameDevKicks.com
# august 3, 2007 12:19

Blog'A'Little said:

I've upgraded the tutorial project to support Silverlight 1.1 Alpha Refresh for the spinning cube tutorial

kick it on DotNetKicks.com kick it on GameDevKicks.com
# august 12, 2007 6:18

http://msn-us.info said:

# august 14, 2008 4:03

http://gay-parad.info said:

# august 18, 2008 9:53

http://buy-viagara.info said:

# august 19, 2008 11:20

http://kukannnn.com said:

c3Pa48 sdfkdf wmlesvnhf dgjotdbm

kick it on DotNetKicks.com kick it on GameDevKicks.com
# august 26, 2008 1:47

http://google-ca.info said:

# august 28, 2008 10:28

YNGdcGbdiVzAjRmIjOk said:

KNPll6

kick it on DotNetKicks.com kick it on GameDevKicks.com
# oktober 8, 2008 5:43

elephantpets said:

all clean day you steven this yahoo juicy ugly letter

kick it on DotNetKicks.com kick it on GameDevKicks.com
# oktober 22, 2008 10:16

Very nice site! said:

Very nice site!

kick it on DotNetKicks.com kick it on GameDevKicks.com
# oktober 25, 2008 12:18

aGRTvaecjKfRlmqxf said:

aNsFmv

kick it on DotNetKicks.com kick it on GameDevKicks.com
# oktober 29, 2008 10:47

zCSDZnuTFjrh said:

JNrxS6

kick it on DotNetKicks.com kick it on GameDevKicks.com
# oktober 29, 2008 2:12

JwStyCMMlUxlmNh said:

BzVLUi

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 4, 2008 2:34

Very nice site! said:

Very nice site!

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 6, 2008 5:09

Very interesting site. Hope it will always be alive! <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

Very interesting site. Hope it will always be alive!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 8, 2008 8:54

If you have to do it, you might as well do it right <a href="http://forums.tvgasm.com/member.php?u=11790">cheap viagra</a> said:

If you have to do it, you might as well do it right

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 9, 2008 12:13

I bookmarked this guestbook. Thank you for good job! <a href="http://forums.tvgasm.com/member.php?u=11790">cheap viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 9, 2008 9:13

I bookmarked this guestbook. Thank you for good job! <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 9, 2008 12:34

Very interesting site. Hope it will always be alive! <a href="http://forums.tvgasm.com/member.php?u=11790">cheap viagra</a> said:

Very interesting site. Hope it will always be alive!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 9, 2008 7:27

I bookmarked this guestbook. Thank you for good job! <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 9, 2008 10:49

If you have to do it, you might as well do it right <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

If you have to do it, you might as well do it right

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 10, 2008 3:47

If you have to do it, you might as well do it right <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

If you have to do it, you might as well do it right

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 10, 2008 8:11

Great work,webmaster,nice design! <a href="http://forum.indya.com/member.php?u=131380">buy viagra</a> said:

Great work,webmaster,nice design!

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 11, 2008 8:36

Very interesting site. Hope it will always be alive! <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

Very interesting site. Hope it will always be alive!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 11, 2008 9:51

I bookmarked this guestbook. Thank you for good job! <a href="http://forum.indya.com/member.php?u=131380">cheap viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 3:24

Excellent site. It was pleasant to me. <a href="http://forum.indya.com/member.php?u=131380">cheap viagra</a> said:

Excellent site. It was pleasant to me.

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 8:53

Very interesting site. Hope it will always be alive! <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

Very interesting site. Hope it will always be alive!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 10:43

If you have to do it, you might as well do it right <a href="http://forum.indya.com/member.php?u=131380">cheap viagra</a> said:

If you have to do it, you might as well do it right

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 11:58

I bookmarked this guestbook. Thank you for good job! <a href="http://forum.indya.com/member.php?u=131380">buy viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 3:04

I bookmarked this guestbook. Thank you for good job! <a href="http://forum.indya.com/member.php?u=131380">cheap viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 5:23

Very interesting site. Hope it will always be alive! <a href="http://www.rc411.com/forum/member.php?u=18196">buy viagra</a> said:

Very interesting site. Hope it will always be alive!

<a href="www.rc411.com/.../member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 8:59

Great work,webmaster,nice design! <a href="http://forum.indya.com/member.php?u=131380">buy viagra</a> said:

Great work,webmaster,nice design!

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 12, 2008 11:44

If you have to do it, you might as well do it right <a href="http://forum.indya.com/member.php?u=131380">cheap viagra</a> said:

If you have to do it, you might as well do it right

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 13, 2008 2:46

Very interesting site. Hope it will always be alive! <a href="http://www.rc411.com/forum/member.php?u=18196">buy viagra</a> said:

Very interesting site. Hope it will always be alive!

<a href="www.rc411.com/.../member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 13, 2008 3:15

I bookmarked this guestbook. Thank you for good job! <a href="http://forums.tvgasm.com/member.php?u=11790">buy viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="forums.tvgasm.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 13, 2008 3:20

I bookmarked this guestbook. Thank you for good job! <a href="http://www.rc411.com/forum/member.php?u=18196">buy viagra</a> said:

I bookmarked this guestbook. Thank you for good job!

<a href="www.rc411.com/.../member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 13, 2008 4:06

Excellent site. It was pleasant to me. <a href="http://forum.indya.com/member.php?u=131380">cheap viagra</a> said:

Excellent site. It was pleasant to me.

<a href="forum.indya.com/member.php viagra</a>

kick it on DotNetKicks.com kick it on GameDevKicks.com
# november 13, 2008 6:00