Charles Petzold

TextureCoordinates and TileBrushes

December 15, 2006
Roscoe, NY

Yesterday I discussed how the TextureCoordinates collection of the MeshGeometry3D class can actually have coordinates outside the range of 0 and 1, contrary to the documentation. I said coordinates outside this range were "internally normalized." Of course, that was a supposition because I am not privy to WPF source code, and today I think that supposition was not quite conceptually correct.

I think it's probably more accurate to say that the complete range of TextureCoordinates values defines a 2D coordinate system for the brush. In many cases, the actual extents of this coordinate system are irrelevant because by default brushes stretch to fill their content, which in this case is the coordinate system implied by the TextureCoordinates collection.

But sometimes brushes can be affected by the coordinate system. In 2D graphics programming, that coordinate system is 96 DPI. In 3D programming, that coordinate system is defined by TextureCoordinates collection.

Consider this XAML file:

The program defines a 10 x 10 unit GeometryDrawing as a resource and then uses that in two DrawingBrush objects with TileMode set to Tile. These two DrawingBrush objects cover two GeometryModel3D objects. In both, the mesh geometries define rectangular surfaces 2 units wide and 4 units deep.

In the first object, the TextureCoordinates collection is defined as the documentation suggests with coordinates between 0 and 1. The Viewport of the DrawingBrush is defined as "0 0 0.1 0.05", which means that each tile occupies 1/10th of the width and 1/20 of the depth of the surface. Consequently, the surface is covered with 10 tiles across the width, and 20 tiles down the depth, which results in the tiles appearing square.

In the second object, the TextureCoordinates are defined with X values ranging from 0 to 100, and Y values from 0 to 50. The ViewportUnits property of the DrawingBrush is set to "Absolute" and the Viewport property is "0 0 10 2.5", which means that each tile is 10 units wide and 2.5 units deep. These units are relative to the coordinate system defined by the TextureCoordinates collection (which is 100 wide and 50 deep), so again there are 10 tiles across the width and 20 tiles down the depth. The result is the same as the first object.

In the second object, you can set ViewportUnits to the default "RelativeToBoundingBox" (or leave it out) and Viewport to "0 0 0.1 0.05" and you'll get the same result. In the first object, it doesn't matter what you set ViewportUnits to because the coordinate system defined by the TextureCoordinates collection has rendered the two options the same.

I haven't seen any advantage to defining TextureCoordinates values outside the range of 0 and 1, and I don't think there is one. But I think this example provides a little bit of insight into how 2D brushes are used by WPF 3D.