2007-07-16 14:48:09
4: Drawing Text in a 3D Scene
This entry is part 4 of a 12-part series on WPF 3D.
Drawing Text in a 3D Scene
This piece of oak is 4 inches wide, 8 inches long, and 1 inch thick. But that's obvious from the picture, right?
As I was learning WPF, it took me a long time to add annotations to Sawdust. I
procrastinated because I assumed it would be difficult to get text into my 3D
scene. Silly me.
In WPF 3D, drawing text is remarkably simple. Basically you just make a VisualBrush from a TextBlock. Use that Brush in a Material and set the TextureCoordinates properly.
Today I'll let the code speak for itself.
/// <summary>
///
Creates a ModelVisual3D containing a text label.
/// </summary>
///
<param
name="text">The string</param>
///
<param
name="textColor">The color of the text.</param>
///
<param
name="bDoubleSided">Visible from both sides?</param>
///
<param
name="height">Height of the characters</param>
///
<param
name="center">The center of the label</param>
///
<param
name="over">Horizontal direction of the label</param>
///
<param
name="up">Vertical direction of the label</param>
///
<returns>Suitable for
adding to your Viewport3D</returns>
public static
ModelVisual3D CreateTextLabel3D(
string text,
Brush textColor,
bool bDoubleSided,
double height,
Point3D center,
Vector3D over,
Vector3D up)
{
// First we need a textblock containing the text
of our label
TextBlock tb = new TextBlock(new Run(text));
tb.Foreground = textColor;
tb.FontFamily = new FontFamily("Arial");
// Now use that TextBlock as the brush for a
material
DiffuseMaterial mat = new DiffuseMaterial();
mat.Brush = new VisualBrush(tb);
// We just assume the characters are square
double width = text.Length * height;
// Since the parameter coming in was the center
of the label,
// we need to find the four corners
// p0 is the lower left corner
// p1 is the upper left
// p2 is the lower right
// p3 is the upper right
Point3D p0 = center - width / 2 *
over - height / 2 * up;
Point3D p1 = p0 + up * 1 * height;
Point3D p2 = p0 + over * width;
Point3D p3 = p0 + up * 1 * height + over *
width;
// Now build the geometry for the sign. It's
just a
// rectangle made of two triangles, on
each side.
MeshGeometry3D mg = new MeshGeometry3D();
mg.Positions = new Point3DCollection();
mg.Positions.Add(p0); // 0
mg.Positions.Add(p1); // 1
mg.Positions.Add(p2); // 2
mg.Positions.Add(p3); // 3
if (bDoubleSided)
{
mg.Positions.Add(p0); // 4
mg.Positions.Add(p1); // 5
mg.Positions.Add(p2); // 6
mg.Positions.Add(p3); // 7
}
mg.TriangleIndices.Add(0);
mg.TriangleIndices.Add(3);
mg.TriangleIndices.Add(1);
mg.TriangleIndices.Add(0);
mg.TriangleIndices.Add(2);
mg.TriangleIndices.Add(3);
if (bDoubleSided)
{
mg.TriangleIndices.Add(4);
mg.TriangleIndices.Add(5);
mg.TriangleIndices.Add(7);
mg.TriangleIndices.Add(4);
mg.TriangleIndices.Add(7);
mg.TriangleIndices.Add(6);
}
// These texture coordinates basically stretch
the
// TextBox brush to cover the full side of
the label.
mg.TextureCoordinates.Add(new Point(0, 1));
mg.TextureCoordinates.Add(new Point(0, 0));
mg.TextureCoordinates.Add(new Point(1, 1));
mg.TextureCoordinates.Add(new Point(1, 0));
if (bDoubleSided)
{
mg.TextureCoordinates.Add(new Point(1, 1));
mg.TextureCoordinates.Add(new Point(1, 0));
mg.TextureCoordinates.Add(new Point(0, 1));
mg.TextureCoordinates.Add(new Point(0, 0));
}
// And that's all. Return the result.
ModelVisual3D mv3d = new ModelVisual3D();
mv3d.Content = new GeometryModel3D(mg,
mat);;
return mv3d;
}