PETZOLD BOOK BLOG

Charles Petzold on writing books, reading books, and exercising the internal UTM


Recent Entries
< PreviousBrowse the ArchivesNext >
Subscribe to the RSS Feed

A Rotating Wireframe Cube for Silverlight 3

July 26, 2009
Roscoe, N.Y.

It took awhile, but I managed to make a rotating wireframe cube using the new PlaneProjection class in Silverlight 3:


RotatingWireframeCube.html

The cube has perspective (that is, the rear is smaller than the front, and as it rotates, these sizes change) but something funny is going on with the lines, and they often shrink in thickness to practically nothingness. Unfortunately, this effect messes up the visual cues we use to separate froreground from background, and if you're not careful, you may start seeing something that looks like an slightly irregular hexahedron rather than a cube.

I won't post the project because the whole thing was done in XAML:

<UserControl 
        x:Class="RotatingWireframeCube.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <Style TargetType="Rectangle" x:Key="rectangleStyle">
            <Setter Property="Width" Value="200" />
            <Setter Property="Height" Value="200" />
            <Setter Property="Stroke" Value="Blue" />
            <Setter Property="StrokeThickness" Value="4" />
        </Style>
    </UserControl.Resources>
    
    <Grid x:Name="LayoutRoot">
        <Grid HorizontalAlignment="Center"
              VerticalAlignment="Center">
            <Rectangle Style="{StaticResource rectangleStyle}">
                <Rectangle.Projection>
                    <PlaneProjection x:Name="front"
                                     LocalOffsetZ="100"
                                     RotationX="30" />
                </Rectangle.Projection>
            </Rectangle>

            <Rectangle Style="{StaticResource rectangleStyle}">
                <Rectangle.Projection>
                    <PlaneProjection x:Name="top"               
                                     LocalOffsetZ="100" 
                                     RotationX="-60" />
                </Rectangle.Projection>
            </Rectangle>

            <Rectangle Style="{StaticResource rectangleStyle}">
                <Rectangle.Projection>
                    <PlaneProjection x:Name="bottom"
                                     LocalOffsetZ="100" 
                                     RotationX="120" />
                </Rectangle.Projection>
            </Rectangle>

            <Rectangle Style="{StaticResource rectangleStyle}">
                <Rectangle.Projection>
                    <PlaneProjection x:Name="back"
                                     LocalOffsetZ="-100"
                                     RotationX="30" />
                </Rectangle.Projection>
            </Rectangle>
        </Grid>
    </Grid>
    
    <UserControl.Triggers>
        <EventTrigger>
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="front"
                                     Storyboard.TargetProperty="RotationY"
                                     By="360" Duration="0:0:10"
                                     RepeatBehavior="Forever" />

                    <DoubleAnimation Storyboard.TargetName="top"
                                     Storyboard.TargetProperty="RotationY"
                                     By="360" Duration="0:0:10"
                                     RepeatBehavior="Forever" />

                    <DoubleAnimation Storyboard.TargetName="bottom"
                                     Storyboard.TargetProperty="RotationY"
                                     By="360" Duration="0:0:10"
                                     RepeatBehavior="Forever" />

                    <DoubleAnimation Storyboard.TargetName="back"
                                     Storyboard.TargetProperty="RotationY"
                                     By="360" Duration="0:0:10"
                                     RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </UserControl.Triggers>
</UserControl>

As you can see, the cube consists of four rectangles (with names "front," "top," "bottom," and "back") that are basically drawn on top of each other and then shifted into place using PlaneProjection to form the cube. The front one is moved (conceptually speaking) 100 pixels closer to the user. The top one is also, and then rotated –90 degrees around the X-axis. The bottom one is the same but rotated a positive 90 degrees. The back one is moved 100 pixels away from the user. In addition, all the sides are rotated an additional 30 degrees around the X-axis to make the rotation more interesting. The animations then rotate each of the rectangles around the Y-axis.

It's really rather simple, so why did it take me 4 hours to get it right? Part of it was a learning experience, of course. I started out using the Matrix3DProjection and abandoned that because it wasn't doing quite what I wanted. (I'll have to investigate this more fully, but I believe that the OffsetZ member of Matrix3D is limited to values between 0 and 1.) So I switched to the PlaneProjection class, which is probably wonderful for simple flips and such, but it's not really built for composite transforms. For example, rotation around the X-axis is applied before rotation around the Y-axis, and you can't change that.

The major discovery I made — and in retrospect I should have expected this, but I had to try it anyway — is that Projection values aren't nestable. I first thought that I would build the cube in a Grid and then apply an animation to the Projection object of the Grid. This doesn't work. The flattening of the projected image to the XY plane occurs on the element level, so what you end up animating is the 2D view, not the 3D coordinates.


Comments:

Why did you want to use Silverlight for this?

— Johnny .no, Sun, 26 Jul 2009 17:57:35 -0400 (EDT)

If I wanted the best platform for 3D graphics obviously I wouldn't use Silverlight. I wrote a whole book on WPF 3D, so obviously I know how to do it that way. But in the context of Silverlight, it's fun figuring out how to do simple 3D tasks with new classes that obviously aren't built for that purpose. — Charles

I wonder if you used 6 rectangles rather than 4 that the thin line problem would be solved? Shot in the dark, but it seems like it might be the problem

Doogal, Sun, 26 Jul 2009 17:57:36 -0400 (EDT)

I original did have six rectangles, and I saw the same problem. I removed the left and right rectangles because I needed to move them into place by rotating around the Y axis, which meant that I couldn't bump the X-axis rotation up by 30 degrees to move the square off-center, because X-axis rotation occurs before Y-axis rotations. — Charles

Visually, the shrinking line problem appears to me like the entire plane, including the 4 thick blue border, is being reprojected. Strange that drawing six squares didn't solve the problem.

— Dave S, Sun, 26 Jul 2009 19:09:24 -0400 (EDT)

My mistake: Having all 6 sides actually does look a lot better: RotatingWireframeCube2.html — Charles

Cool project... I'd be curious to see how complex the "real" 3d version of it would be, since silvelright 3 is now supposed to support 3d?

boomhauer, Mon, 3 Aug 2009 00:34:34 -0400 (EDT)

Silverlight 3 does not support 3D except for perspective effects. — Charles

Yep just read more about it. Every ref to 3d has "perspective" in front of it...

boomhauer, Mon, 3 Aug 2009 08:21:02 -0400 (EDT)

Sometimes when you look at it it spins in the opposite direction and looks out of shape

Tim, Tue, 22 Sep 2009 19:58:16 -0400 (EDT)

Hi, Pls update the source code for the 6 phases RotatingWireframeCube...

— Cindy, Thu, 22 Oct 2009 05:17:21 -0400

I can only imagine what whent through your mind the first time your eyes caught the perspective problem and made it rotate counterclockwise and out of shape. I've seen some bugs in code, but never a optical illusion. Bet you really started scratching your head!

— Paul, Wed, 18 Nov 2009 20:52:54 -0500


Recent Entries
< PreviousBrowse the ArchivesNext >
Subscribe to the RSS Feed

(c) Copyright Charles Petzold
www.charlespetzold.com