April 2006 - Posts

One of the capabilities that WPF provides is the ability to render 2D visual elements on 3D surface. For example, you can place panels, text blocks, images, or even videos on the surface of a thin box or "screen" (think video carousel), or even on a sphere or cylinder if desired. In order to put visual elements on your 3D objects, you need to use texture coordinates in your mesh geometry.

Texture coordinates map the Cartesian positions of your 3D mesh to the Cartesian positions of your visual element. The easiest way to explain this is with an example. Let's say you have a 3D model that defines a plane that is centered about the X,Y,Z origin:

Plane Coordinates
A plane's mesh geometry.

Now let's say you have a visual element (e.g. a text block or image) that you want to use as the material for your plane. The visual element's positions are defined by the X,Y coordinate convention used in images - that is, the positive Y axis points "down":

Visual Coordinates
A visual's coordinates.

To get the image or text block on to the plane mesh, the plane mesh needs to define how its 3D positions map to conventional 2D coordinates used in images. Basically we will need this mapping:

3D Plane Position 2D Visual Coordinate
(-1,-1) (0,1)
(1,-1) (1,1)
(1,1) (1,0)
(-1,1) (0,0)

When you define a mesh using WPF, the order in which you add positions, triangle indices, normals, and texture coordinates is important. As you add positions to the mesh, the index of the position in the mesh's Positions collection equal the indices of the points in the mesh's TextureCoordinates collection.

The XAML for the plane's mesh geometry would look like this if you define the positions starting from the (-1,-1) position and work your way counter-clockwise. The triangles defined in the mesh are { (-1,-1) (1,-1) (1,1) } and { (-1,-1) (1,1) (-1,1) }:


<GeometryModel3D 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <GeometryModel3D.Geometry>
    <MeshGeometry3D >
      <MeshGeometry3D.Positions>
        <Point3D X="-1" Y="-1" Z="0"/>
        <Point3D X="1" Y="-1" Z="0"/>
        <Point3D X="1" Y="1" Z="0"/>
        <Point3D X="-1" Y="1" Z="0"/>
      </MeshGeometry3D.Positions>
      <MeshGeometry3D.TextureCoordinates>
        <Point X="0" Y="1"/>
        <Point X="1" Y="1"/>
        <Point X="1" Y="0"/>
        <Point X="0" Y="0"/>
      </MeshGeometry3D.TextureCoordinates>
      <MeshGeometry3D.TriangleIndices>
        0,1,2 0,2,3
      </MeshGeometry3D.TriangleIndices>
      <MeshGeometry3D.Normals>
        <Vector3D X="0" Y="0" Z="1"/>
        <Vector3D X="0" Y="0" Z="1"/>
        <Vector3D X="0" Y="0" Z="1"/>
        <Vector3D X="0" Y="0" Z="1"/>
        <Vector3D X="0" Y="0" Z="1"/>
        <Vector3D X="0" Y="0" Z="1"/>
      </MeshGeometry3D.Normals>
    </MeshGeometry3D>
  </GeometryModel3D.Geometry>
</GeometryModel3D>    
    

With this mesh, a 2D visual now has a mapping for its coordinates against the 3D surface.

Now all you need to do is add the visual to the surface as a VisualBrush for the material:


<GeometryModel3D.Material>
  <MaterialGroup>
    <DiffuseMaterial>
      <DiffuseMaterial.Brush>
        <SolidColorBrush Color="Gray"/>
      </DiffuseMaterial.Brush>
    </DiffuseMaterial>
    <DiffuseMaterial>
      <DiffuseMaterial.Brush>
        <VisualBrush>
          <VisualBrush.Visual>
            <TextBlock 
                Margin="5" 
                Foreground="Black" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Center" 
                TextAlignment="Center" 
                FontWeight="Bold" 
                FontSize="8pt">
                The Melvins
            </TextBlock>
          </VisualBrush.Visual>
        </VisualBrush>
      </DiffuseMaterial.Brush>
    </DiffuseMaterial>
  </MaterialGroup>
</GeometryModel3D.Material>
    

This will generate a very simple (and very "raw") model with text:

Visual Brush Screenshot
A 3D surface with a VisualBrush

While texture coordinates are a fundamental part of decorating 3D models with graphics or placing complex UI layouts on a 3D surface, they can be overlooked by the developer who is diving in to 3D modeling for the first time. In real 3D models, geometries will be more complex and mapping texture coordinates won't be as simple as the four corners of a plane... and in all likelyhood a designer will be doing that dirty work for you in a 3D modeling tool. However, I think it's always good to have an understanding of what's happening "under the hood" so that you can make better decisions about how to go about your work. Hopefully this example will help you as you create interesting user interfaces in WPF.

with 5 comment(s)
Filed under:

If you're a developer, you probably open up a lot of files in Notepad (or Notepad2, or your favorite text editor). There are a lot of files in Windows that aren't normally associated with Notepad though, so you sometimes have to go through the process of associating the extension with Notepad or selecting Notepad from a list of applications when you want to view a file.

There's a shortcut around this: just add a shortcut to Notepad.exe to your Send To folder. Send To is located under:

c:\documents and settings\[username]\SendTo

Just open the folder and create a shortcut to Notepad. Then all you need to do is right-click on any file and choose Send To -> Notepad.