Click here to Skip to main content
15,879,239 members
Articles / Desktop Programming / XAML
Article

A Textured Triangle Control for Silverlight 2 - the Basic Building Block for 3D

Rate me:
Please Sign up or sign in to vote.
4.68/5 (14 votes)
11 Jun 2008Ms-PL5 min read 87.9K   488   44   14
This article is all about implementing a triangle primitive as a custom control in Silverlight 2.0 that can be used for 3D effects in Silveright.

Image 1

Introduction

In contrast to WPF, there's no support for 3D in Silverlight 2.0. Still, it has the power of the .NET runtime built-in, so there's nothing to stop you from rolling your own little 3D engine in Silverlight. Not a high end one, but something to play around with and to add some neat effects to your RIA.

All you need is some understanding of math and the basic primitive of all things 3D - the triangle primitive. Specifically, I'm talking about a textured triangle with an image mapped on it.

Consequently, this article is all about implementing a triangle primitive as a custom control in Silverlight 2.0.

Using the triangle control

Using the control from XAML

Here's a snippet that shows how the ImageTriangle control can be used directly in XAML:

XML
<UserControl x:Class="Controls.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:xk="clr-namespace:XamlKru.Controls.Primitives;assembly=XamlKru.Controls"
>
    <Canvas ...>
        <xk:ImageTriangle Point1="0,0" Point2="180,20" Point3="0,200" 
                          TexturePositions="0,0 1,0 0,1" 
                          TextureSource="/Controls;component/checker.jpg" 
        />
    </Canvas>
</UserControl>

As always, when you reference custom classes from XAML, you must add a namespace reference to the control assembly first:

XML
xmlns:xk="clr-namespace:XamlKru.Controls.Primitives;assembly=XamlKru.Controls"

You can then reference the triangle in XAML with <xk:ImageTriangle ... /> and set the corner points (Point1, Point2, and Point3) via attributes as well as the TextureSource. TextureSource is an ImageSource that takes the image that will be rendered as texture onto the triangle. You can also set the texture coordinates by setting the TexturePositions, e.g., TexturePositions="0,0 1,0 0,1".

Using the control from code

Doing that in XAML is perfect for playing around with the control and getting to know how the various properties work and play together. However, when you want to use it for 3D or something similar, you're going to do it in code, be it C#, VB, or maybe IronPython.

In addition to the properties mentioned above, you can set all three corner points in a single method call in imperative code, which has some performance advantages:

C#
tri1.SetPoints(new Point(0,0), new Point(100,0), new Point(0,100));

The TextureSource can be set directly to a BitmapImage, which allows you to use images downloaded from the web, or even from the local file system, as textures:

C#
var texture = new BitmapImage();
texture.SetSource(inputStream);
tri1.TextureSource = texture;

There is also an IsClockwise property that you might want to use for backface culling in 3D.

C#
tri.IsClockwise

How does it work?

How would you draw a textured triangle in Silverlight? There are two ways I could think of:

  • use an Image control, and clip half of it away with a clipping path
  • Use a Path to draw the triangle shape, and add the image as an ImageBrush

I chose the second way, because it seemed to be a little bit faster.

Point1, Point2 and Point3 are implemented as Dependency Properties. Dependency Properties allow to perform calculations in the PropertyChanged callback and add the capability to take part in data-binding and animations.

In order to transform the triangle to fit the three corner points, I've added a MatrixTransform to the Path-element's RenderTransform. It is fairly straightforward to calculate the matrix elements from the given points. An article that explains how that works can be found here.

When you look at the control template and the UpdateCorners method, you will notice that there's a second transform applied as RenderTransform. When I got the code to work for the first time and created two triangles next to each other, I noticed a small, but irritating seam between them. This is due to how the antialiasing works in Silverlight. That's what the second transform is for. It scales the triangle up just a little bit (0.5 - 1 pixels on each side), just enough to overlap the seam.

Adding the texture

Great, now we've got a triangle that we can position arbitrarily. Are we done? Actually, no! Up to that point, the image on the triangle is fixed. What if you want to create a plane made of two triangles (or better 4 or 16, as I will explain below) - you don't want to load a separate image for each half!

We need a way to stretch and move the image position on the triangle itself. We need texture coordinates.

Again, this is done with a MatrixTransform. This time it is applied to the ImageBrush, filling the Path with a texture image. In this case, it is a little bit more tricky, though.

Effectively, what texture coordinates say is, where on the texture image the triangle lies. For the matrix applied to the ImageBrush, it has to be the other way round, we want to position the image as seen from the triangle's coordinate system.

triangle_textures.jpg

We can do this by first calculating the matrix that maps the triangle onto the image and then inverting that matrix to get a transformation that maps the image onto the triangle. Exactly this is what's happening in the UpdateTexturePositions method:

C#
var m = new Matrix(m11, m12, m21, m22, ox, oy).Invert();
_brushTransform.Matrix = m;

Limitations

When you create a plane from two triangles and transform it to 3D, you might notice that it doesn't look quite right. This is because the depth information is not used. The only way to get around that is by adding more triangles. If you use eight triangles instead of two, the texture will look more realistic in 3D. You have to find the right tradeoff between performance and visual correctness.

triangle_distortion.gif

Moving on

This article doesn't explain how to do 3D, it just provides you with a basic building block! However, the project files contain some 3D code to get you started.

My experiments have shown that rendering something around 100 triangles is possible at an acceptable frame-rate on a recent machine. Nothing to get too excited about, you won't be able to build a Halo or Second Life in Silverlight anytime soon. At least, not on a PC. But, it will be enough to do Coverflow-style animations, spinning 3D globes, and 360° panoramas, or maybe even games.

History

  • First version posted by Florian Krüsch on May 03, 2008.
  • Updated the code in alignment with Silverlight 2 Beta 2 on June 11, 2008.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Web Developer
Germany Germany
Florian Krüsch works as a Freelance Software Architect, Developer and Consultant in Düsseldorf, Germany.

He is excited about WPF, Silverlight and ASP.net as well as Enterprise Development on the .NET platform.

When not thinking, coding or listening to podcasts, he enjoys spending his time with his family and his little son Nicki.



Comments and Discussions

 
QuestionQuestion about creating a Triangle Image in Code Pin
simon.roderus2-Jul-08 20:53
simon.roderus2-Jul-08 20:53 
Hello,

I tried to create a triangle control in code - but nothing seems to happen (I see no visible result). If I create the same control in XAML it works perfect. Does anyone know what I am doing wrong?

XAML:
< Grid x:Name="LayoutRoot" Background="White">
    < xk:ImageTriangle x:Name="MyImageTriangle"
                      Point1="0,0" Point2="180,20" Point3="0,200"
                      TexturePositions="0,0 1,0 0,1"
                      TextureSource="images/blue hills.jpg" />

</Grid>


C#:
i2 = new ImageTriangle();

i2.SetPoints(new Point(0, 0), new Point(180, 20), new Point(0, 200));

PointCollection p = new PointCollection();
p.Add(new Point(0, 0));
p.Add(new Point(1, 0));
p.Add(new Point(0, 1));
i2.TexturePositions = p;

i2.TextureSource = new BitmapImage(new Uri("images/blue hills.jpg", UriKind.Relative));

LayoutRoot.Children.Add(i2);

QuestionRe: Question about creating a Triangle Image in Code Pin
simon.roderus7-Jul-08 21:57
simon.roderus7-Jul-08 21:57 
AnswerRe: Question about creating a Triangle Image in Code Pin
Dewey12-Jul-08 13:53
Dewey12-Jul-08 13:53 
QuestionNot working with latest Silverlight Version Pin
simon.roderus10-Jun-08 21:44
simon.roderus10-Jun-08 21:44 
AnswerRe: Not working with latest Silverlight Version Pin
Florian Kruesch11-Jun-08 1:44
Florian Kruesch11-Jun-08 1:44 
GeneralRe: Not working with latest Silverlight Version Pin
simon.roderus30-Jun-08 21:23
simon.roderus30-Jun-08 21:23 
GeneralRe: Not working with latest Silverlight Version Pin
Waaahsabi25-Nov-08 23:27
Waaahsabi25-Nov-08 23:27 
GeneralRe: Not working with latest Silverlight Version Pin
Waaahsabi26-Nov-08 1:36
Waaahsabi26-Nov-08 1:36 
GeneralNice Work Pin
Whizzkid5-May-08 22:37
Whizzkid5-May-08 22:37 
GeneralRe: Nice Work Pin
Florian Kruesch5-May-08 23:30
Florian Kruesch5-May-08 23:30 
GeneralA live demo would be great! Pin
Uwe Keim3-May-08 11:51
sitebuilderUwe Keim3-May-08 11:51 
QuestionRe: A live demo would be great! Pin
jeep.he3-May-08 20:48
jeep.he3-May-08 20:48 
AnswerRe: A live demo would be great! Pin
Florian Kruesch4-May-08 22:13
Florian Kruesch4-May-08 22:13 
GeneralValuable article Pin
AdamJTP3-May-08 11:09
AdamJTP3-May-08 11:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.