Wrox Home  
Search
Professional CSS Cascading Style Sheets for Web Design
by Jean-Luc David, Bill Ryan, Ron DeSerranno, Alexandra Young
September 2005, Paperback


Excerpt from Professional WinFX Beta: Covers "Avalon" Windows Presentation Foundation and "Indigo" Windows Communication Foundation

Avalon 3D

The three-dimensional (3-D) elements are derived from System.Windows.Media.Media3D namespace. The 3-D objects created in Avalon can be primitive or models. They can be manipulated with transforms, animation, and hit testing (the ability to mouse click 3-D objects) as well as be made to appear to be on the same plane as other 2-D objects. A 3-D scene comprises a viewing plane, camera, lights, and objects. To view the objects in the scene, you must apply materials and textures to their surfaces so that the lights can be cast across them. Because the 3-D syntax can quickly get involved, the code samples in this article will be fragments focusing on that particular element.

Viewport3D

Before you can create a 3-D environment, you need a viewing plane. The API has two options, the RetainedVisual3D, which creates an environment suitable for 3-D graphs, and the Viewport3D. The latter option is a base class of the System.Windows.Controls namespace and is the rendering plane for 3-D objects. Because Viewport3D is more commonly used in 3-D scenes, all subsequent samples will be based on Viewport3D.

The basic markup for this control is as follows:

<Viewport3D Canvas.Top="50" Canvas.Left="50" Width="300" Height="300" 
    ClipToBounds="true">

     #Scene definition of lights, camera, and objects

</Viewport3D>

Cameras

The Camera is the manner is which a 3-D environment is viewed, or simply put, it serves as your eye. There are two types of camera classes available: ProjectionCamera and the MatrixCamera. The MatrixCamera class is used for applications with built-in matrix calculation devices.

The ProjectionCamera is the base class that depicts the viewer's point of view (POV) and determines how the scene will appear on the viewing surface to the user. Because of the constraints of this article, the focus will be on the ProjectionCamera class of cameras. The following table lists commonly used properties for the cameras derived from this class.

Property Syntax Description
FarPlaneDistance FarPlaneDistance="double" A double type valued property that determines how far the camera is from the scene's horizon line. This property is used with the NearPlaneDistance to define the camera's near and far viewing range (limits).
LookAtPoint LookAtPoint="x,y,z" Determines the camera's direction: The camera will face this point from its position. The format requires values for its X, Y, Z coordinates.
NearPlaneDistance NearPlaneDistance="double" A double type valued property that determines how close the camera is to the scene. This property is used with FarPlaneDistance to define the camera's near and far viewing range (limits).
Position Position="x, y, z" This property determines the location of the Camera within the scene. The format requires values for its X, Y, Z coordinates.
Up Up="x,y,z" Determines the amount of tilt (or roll) to apply to the camera to set the scene (like tilting your head).

Perspective Camera

The PerspectiveCamera and OrthographicCamera are both derived from the ProjectionCamera base class. The PerspectiveCamera is the most versatile and the most commonly used of the two. As its name implies, it uses perspective and foreshortening to make objects appear near to or far from the camera.

<Viewport3D.Camera>
     <PerspectiveCamera Position="-250,250,200" 
	 LookAtPoint="0,0,0" Up="0,1,0" 
        FieldOfView="40" NearPlaneDistance="1" FarPlaneDistance="500" />
</Viewport3D.Camera>

Based on the definitions in the table, the preceding sample code is fairly straightforward. The position of the camera is in the scene and slightly to the left of center with a slight downward tilt. The focal point of the scene is absolute center and the depth of the scene is quite vast because the camera can view from 1 to 500, as determined by NearPlaneDistance and FarPlaneDistance, before objects disappear. Any object outside of this range will not be visible. When setting the range, you have to consider the size of the range because a degree of distortion can occur based on the depth buffer. It has limited accuracy on larger areas, which can cause flickering, jumping, and other anomalies. A property exclusive to the perspective camera is the FieldOfView property. It determines how much of the scene will be seen through the camera along the horizontal plane. (This is similar to using a standard lens versus a wide-angle lens to create the shot).

Orthographic Camera

OrthographicCamera is similar to perspective camera except it flattens everything out (no perspective is apparent). This camera is good for portraying blue prints or data visualization in graphs, because there is no distortion to the object's sizing properties. This camera uses the Width property instead of the FieldOfView property that the PerspectiveCamera uses. Width determines how much of the scene is in view. Although the scene is a 3-D environment, it has a 2-D boxlike appearance in comparison to the PerspectiveCamera. This means that although all the objects in the scene are 3-D, they are flattened out. This particular camera is commonly used for displaying 3-D graphs and charts.

<Viewport3D.Camera>
     <OrthographicCamera Position="-250,250,200" 
	 LookAtPoint="0,0,0" Up="0,1,0" 
        Width="150" NearPlaneDistance="1" FarPlaneDistance="500"/>
</Viewport3D.Camera>

Model3DGroup

Model3DGroup is the class that contains all the children (both the objects and lights) in the scene.

<Viewport3D>
     <!--Camera defined here-->
     <Viewport3D.Models>
 
          <Model3DGroup >
               <Model3DGroup.Children>

                    <!-- Insert models and Lights here -->

               </Model3DGroup.Children>
          </Model3DGroup>

     </Viewport3D.Models>
</Viewport3D>

Lights

Located in the System.Windows.Media.Media3D namespace, these classes provide the illumination to a 3-D environment. There are three base classes that are derived from the Light class: AmbientLight, DirectionalLight, and PointLight. Lighting in a 3-D scene is required to render the scene. In general, the lights shine across objects with a material and texture. If an object is without either, it will not be seen.

Ambient Light

The first and easiest to use of all the base classes is the AmbientLight. It is a universal light (like daylight) that affects the entire scene equally despite the size or shape of the objects; therefore, this light does not have a positioning property.

<AmbientLight Color="#C0C0C0" />

Directional Light

Another ever-present light that lights all the objects in a scene is the DirectionalLight. It differs from the ambient because the light is cast from a specified direction (X, Y, Z format) as opposed to the universality of the AmbientLight. Therefore, an object's surface that is directly facing into the light source will be illuminated. Any surface not facing into the source will be cast in shadows. This light is effective in portraying the rays of light from the sun.

<DirectionalLight Color="#C0C0C0" Direction="-0.5,-0.25,-0.25"/>

Point Light

The PointLight is a single point light source that casts its light in a uniform fashion throughout the 3-D scene. This light is more involved than the other two because it requires specification to each of its properties, a position, color, range, and attenuation values. The following table lists properties exclusive to this light class.

Property Description
ConstantAttenuation This property determines the intensity of the light as it travels through space toward the object it is to illuminate. Logically speaking, as the distance from the light to the object increases, the intensity will decrease. This is a double type valued property with a range between 0 and 1.
LinearAttenuation Determines the intensity of light multiplied with the distance from the source to the object. This property has an exponential double value range beginning from 0.001, and the light gets more intense as the number gets smaller.
Position Determines the exact position of the light in the scene. The placement is determined by an X, Y, Z format.
QuadraticAttenuation Determines the intensity of light multiplied with the square of the distance from the source to the object. This property has an exponential double value range beginning from 0.0001, and the light gets more intense as the number gets smaller.
Range A double type valued property that determines the distance the light will travel. If an object is outside of the range of the light it will not be seen.

The point light is the 3-D equivalent of a light bulb. This light is optimal for illuminating objects that require precision lighting to create a sense of drama to the scene, because it has many definable properties.

<PointLight Position="0,100,0" 
Color="#C0C0C0" Range="100" 
LinearAttenuation="1" />

Spot Light

SpotLight is an additional light that is derived from the PointLight class. This produces the most intense of all the lights. Listed in the following table are additional properties present that further pinpoint the light SpotLight casts on objects.

Property Description
Direction Defines the direction the light is to travel from the light source to the object.
InnerConeAngle Determines the angle of the most intense part (hot spot) of the light in relation to the rest of the light. In other words, as an object gets closer to the InnerConeAngle, the more washed out it will appear.
OuterConeAngle Defines the angle of the cone-shaped projection from the light. This light is so exact that objects or other areas of the object immediately outside the scope of the OuterConeAngle will appear in shadow.
<SpotLight Position="0,100,0" Direction="0,-1,0" 
Color="#C0C0C0" Range="100"  
   ConstantAttenuation="1" InnerConeAngle="20" OuterConeAngle="35"/>

3-D Objects

A 3-D object is composed of a mesh, texture, and material. They are located in the System.Windows.Media.Media3d namespace, and all objects in a scene are contained in the MeshGeometry3D base class. The purpose of this base class is to determine how the shape is to be shown, whether it will be created with lines, points, or triangles.

The Positions property is used in conjunction with TriangleIndices to define the collection of points that make up the object. The format of the Positions property is a collection of three double typed values to represent the X, Y, Z coordinates to create the shape. The format of TriangleIndices is also in this format; however, its numbers act as a guide to place the triangle on the point created by the Positions property. Normals, also fashioned in this manner, are values that act as an indicator to the light source, to dictate how light will be cast over each point on the surface of the object. Each value in the Normals collection directly corresponds to a point in the Positions collection. The values in this property are commonly standardized to a length of 1 but can also be 0 or -1. If the values are the same, this indicates a flat surface.

Because you know a normalized vector has a length of 1, to determine the normalization of each component you would use the Pythagoras theorem (x² + y² + z² = length˛) and then multiply each by 1/length˛.

The TextureCoordinates property directs how the brush is applied to the mesh surface. As with the properties previously mentioned, this property also maps to a point in the Positions collection. The values in this collection are a set of two floats with a double value range between 0 and 1.

Following is a section of code showing each of the object's properties.

<GeometryModel3D >
  <GeometryModel3D.Geometry> 
<MeshGeometry3D Normals="0,0,1 0,0,1 0,0,1 0,0,1" 
   Positions="-10,-10,30 10,-10,30 -10,10,30 10,10,30"                    
   TextureCoordinates="0,1 1,1 0,0 1,0" 
   TriangleIndices="0 2 1 1 2 3" />
  </GeometryModel3D.Geometry>
  <GeometryModel3D.Material>
       <BrushMaterial Brush="#FF008000" />
  </GeometryModel3D.Material>               
</GeometryModel3D>

Figure 1 shows the result of this code.

Figure 1
Figure 1:

Referencing a Mesh

The benefit of referencing a mesh is that it can be defined globally and reused anywhere within the application. Reusable definitions are defined in the Canvas.Resources section of the application and can be applied to any Mesh3D in the application. To Define a referenceable object in 3-D, you use the same format you learned in the Styles section, x:Key="Name of Object". The following code adds a second object to the shape discussed in the earlier section.

<Canvas xmlns="http://schemas.microsoft.com/winfx/avalon/2005" 
   Background="#FFFFFFFF" Height="400" Name="ROOT" Width="500" 
   xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005">
  
 <Canvas.Resources>
      <MeshGeometry3D x:Key="SmallerBox" 
         Positions="-10,-10,80  10,-10,80  -10,10,80  10,10,80"
         Normals="0,0,1  0,0,1  0,0,1  0,0,1" 
         TextureCoordinates="0,1  1,1  0,0  1,0" 
         TriangleIndices="0,2,1  1,2,3" />
 </Canvas.Resources>

 <Panel.Children>
      <Viewport3D Height="137" Canvas.Left="143" 
	  Canvas.Top="109" Width="202">
           <Viewport3D.Camera>
                <PerspectiveCamera FarPlaneDistance="5000" 
				FieldOfView="45" 
                   LookAtPoint="0,0,1" NearPlaneDistance="1" 
                   Position="25,0,-15" Up="0,1,0" />
           </Viewport3D.Camera>
  <Viewport3D.Models>
    <Model3DGroup>
         <Model3DGroup.Children>
              <Model3DCollection>

               <!--Green sample from earlier syntax sample -->
               <GeometryModel3D>
                    <GeometryModel3D.Geometry> 
                         <MeshGeometry3D 
                            Normals="0,0,1 0,0,1 0,0,1 0,0,1" 
                            Positions="-10,-10,30 10,-10,30 
                            -10,10,30 10,10,30"                    
                            TextureCoordinates="0,1 1,1 0,0 
                            1,0" 
                            TriangleIndices="0 2 1 1 2 3" />
                    </GeometryModel3D.Geometry>

                    <!-- Brush applied to surface of object-->
                    <GeometryModel3D.Material>
                         <DiffuseMaterial Brush="#FF008000" />
                    </GeometryModel3D.Material>               
               </GeometryModel3D>

               <!--Referenced Object -->
               <GeometryModel3D 
                  Geometry="{StaticResource SmallerBox}">
                    <GeometryModel3D.Material>
                         <DiffuseMaterial Brush="Pink" />
                    </GeometryModel3D.Material>               
               </GeometryModel3D>

               <!-- Light Source-->
               <AmbientLight Color="#FFFFFFFF" />
              </Model3DCollection>
         </Model3DGroup.Children>
    </Model3DGroup>
  </Viewport3D.Models>
      </Viewport3D>
 </Panel.Children>
</Canvas>

Figure 2 shows the result of this code.

Figure 2
Figure 2:

Materials

The Material represents the surface of a 3-D object. With no material information, a model will most likely end up being rendered entirely black. Applying a material to a mesh is much like applying a brush to a 2-D object. The surfaces of the 3-D objects can be painted with brushes derived from the four brush base classes:

  • GradientBrush
  • NineGridBrush
  • SolidColorBrush
  • TileBrush
<GeometryModel3D.Material>
     <DiffuseMaterial Brush="Red"/>
</GeometryModel3D.Material>

Viewport is an additional property required when applying a brush from the TileBrush base class. It determines the location and dimensions of the tiles produced by the brush.

<GeometryModel3D.Material>
     <DiffuseMaterial>
          <DiffuseMaterial.Brush>
               <ImageBrush Stretch="UniformToFill" 
			   ImageSource="MyImage.jpg" />
          </DiffuseMaterial.Brush>
     </DiffuseMaterial>
</GeometryModel3D.Material>

Transforms

As with 2-D graphics, 3-D objects can also be transformed. In a 3-D scene, all objects are defaulted with an identity matrix, which means the object has a position value of 0,0,0 and has no rotation nor scaling applied. Transforms control the placement, orientation, and sizing and can also be used with animation to move the objects through the scene.

As with the rest of the APIs, when controlling multiple objects, they are held in a collection class. In 3-D the class is called a Transform3DCollection.

Following is the XAML syntax for creating a single transform for a 3-D object.

<GeometryModel3D Mesh="{StaticResource SmallerBox}">
     <Model3D.Transform>
          <TranslateTransform3D Offset="-10,3,30" />
     </Model3D.Transform>       
</GeometryModel3D>

Next is an example of how to write the markup for creating multiple transforms within a Transform3DCollection for a 3-D object:

<GeometryModel3D Geometry="{StaticResource SmallerBox}">
<Model3D.Transform>
     <Transform3DGroup>
          <Transform3DGroup.Children>
               <Transform3DCollection>
                    <TranslateTransform3D Offset="-10,3,30" />
                    <ScaleTransform3D ScaleVector="2 4 2" 
                       ScaleCenter="0,0,0"/>
                    <RotateTransform3D Center="0,0,0">
                         <RotateTransform3D.Rotation>
                              <Rotation3D Axis="0,10,100" 
Angle="45" />
                         </RotateTransform3D.Rotation>
                    </RotateTransform3D>
               </Transform3DCollection>
          </Transform3DGroup.Children>
     </Transform3DGroup>
</Model3D.Transform>
</GeometryModel3D>

Translation

A Translation transform moves an object's location through the scene. Instead of just shifting the 2-D graphic based on its X, Y coordinates, in 3-D it performs the transform to the X, Y, Z coordinates of each point listed in the 3-D object.

<TranslateTransform3D Offset="10 20 0" />

Scale

ScaleTransform3D is the transform used to make objects bigger or smaller. Based on the center point of the object, this transform applies the scaling to each of the mesh's points. The ScaleVector property individually scales the object based on its X, Y, Z values.

  <ScaleTransform3D ScaleVector="2 4 2" ScaleCenter="0,0,0"/>

For example, in the preceding code fragment, the X and Z points will be two times the original size, and the Y points will be four times the original size. Values less than 1 will scale down the size of the object. If ScaleCenter has values other than 0,0,0, the stretching and the object's positioning will be altered.

Rotation

RotateTransform3D uniformly rotates all points of a mesh around its axis. The Angle property determines the amount of incline that will be applied (in degrees) to the object as it is rotated around the Center property. If the Center values are not centered to the object, the object will appear to spin around that point instead of spinning in one spot.

<RotateTransform3D Angle="45" Center="0,0,0" Axis="0,10,100"/>