78 msdn magazine DirectX Factor
CreateWindowSizeDependentResources that converts the source
polygons to the destination polygons based on the size of the screen.
In the inner loop, you’ll see x, y and z values calculated. Th is is
a 3D coordinate but it’s not even saved. Instead, it’s immediately
collapsed back into 2D by simply ignoring the z value. To calcu-
late those 3D coordinates, the code first converts a horizontal
position on the original path geometry to an angle in radians from
0 to 2π. The sin and cos functions calculate a position on a unit
circle on the XZ plane. Th e y value is a more direct conversion from
the vertical coordinates of the original path geometry.
Th e CreateWindowSizeDependentResources method concludes
by obtaining a new ID2D1PathGeometry object from the desti-
nation Polygon collection. Th e Render method then sets a matrix
transform to put the origin in the center of the screen, and both fi lls
and outlines this path geometry, with the result shown in Figure 4.
Is the program working? It’s hard to tell! Look closely and you
can see some wide characters in the center and narrower characters
at the left and right. But the big problem is that I started out with a
path geometry with no intersecting lines, and now the geometry
is displayed back over itself, with the result that overlapping areas
are not fi lled. Th is eff ect is characteristic of geometries, and it hap-
pens whether the path geometry created by the Polygon structure
has a fi ll mode of alternate or winding.
Getting Some Perspective
Th ree-dimensional graphics programming
is not just about coordinate points. Visual
cues are necessary for the viewer to interpret
an image on a 2D screen as representing an
object in 3D space. In the real world, you
rarely view objects from a constant vantage
point. You’d get a better view of the 3D text
in Figure 4 if you could tilt it somewhat so
it looks more like the ring in Figure 1.
To get some perspective on the three-dimensional text, the
coordinates need to be rotated in space. As you know, Direct2D
supports a matrix transform structure named D2D1_MATRIX
_3x2_F that you can use to defi ne 2D transforms, which you can
apply to your 2D graphics output by fi rst calling the SetTransform
method of ID2D1RenderTarget.
Most commonly, you would use a class named Matrix3x2F from
the D2D1 namespace for this purpose. Th is class derives from
D2D1_MATRIX_3x2F_F and provides methods for defi ning var-
ious types of standards for translation, scaling, rotation and skew.
Th e Matrix3x2F class also defi nes a method named Transform-
Point that allows you to apply the transform “manually” to individual
D2D1_POINT_2F objects. Th is is useful for manipulating points
before they’re rendered.
You may think I need a 3D rotation matrix to tilt the displayed
text. I’ll certainly be exploring 3D matrix transforms in future
columns, but for now I can make do with 2D rotation. Imagine
yourself situated somewhere on the negative X axis of Figure 1,
looking toward the origin. Th e positive Z and Y axes are situated
just like the X and Y axes in a conventional 2D coordinate system,
so it seems plausible that by applying a 2D rotation matrix to the
Z and Y values, I can rotate all the coordinates around the three-
dimensional X axis.
Figure 4 The CircularText Display Figure 5 The Tilted CircularText Display
Figure 6 The Update Method of SpinningCircularText
void SpinningCircularTextRenderer::Update(DX::StepTimer const& timer)
{
// Get window size and geometry size
Windows::Foundation::Size logicalSize = m_deviceResources->GetLogicalSize();
float geometryWidth = m_geometryBounds.right - m_geometryBounds.left;
float geometryHeight = m_geometryBounds.bottom - m_geometryBounds.top;
// Calculate a few factors for converting 2D to 3D
float radius = logicalSize.Width / 2 - 50;
float circumference = 2 * 3.14159f * radius;
float scale = circumference / geometryWidth;
float height = scale * geometryHeight;
// Calculate rotation matrix
float rotateAngle = -360 * float(fmod(timer.GetTotalSeconds(), 10)) / 10;
Matrix3x2F rotateMatrix = Matrix3x2F::Rotation(rotateAngle);
// Calculate tilt matrix
Matrix3x2F tiltMatrix = Matrix3x2F::Rotation(m_tiltAngle);
for (size_t polygonIndex = 0; polygonIndex < m_srcPolygons.size(); polygonIndex++)
{
const Polygon& srcPolygon = m_srcPolygons.at(polygonIndex);
Polygon& dstPolygon = m_dstPolygons.at(polygonIndex);
for (size_t pointIndex = 0; pointIndex < srcPolygon.Points.size(); pointIndex++)
{
const D2D1_POINT_2F pt = srcPolygon.Points.at(pointIndex);
float radians = 2 * 3.14159f * (pt.x - m_geometryBounds.left) / geometryWidth;
float x = radius * sin(radians);
float z = radius * cos(radians);
float y = height * ((pt.y - m_geometryBounds.top) / geometryHeight - 0.5f);
// Apply rotation to X and Z
D2D1_POINT_2F rotatedPoint = rotateMatrix.TransformPoint(Point2F(x, z));
x = rotatedPoint.x;
z = rotatedPoint.y;
// Apply tilt to Y and Z
D2D1_POINT_2F tiltedPoint = tiltMatrix.TransformPoint(Point2F(y, z));
y = tiltedPoint.x;
z = tiltedPoint.y;
dstPolygon.Points.at(pointIndex) = Point2F(x, y);
}
}
// Create path geometry from Polygon collection
DX::ThrowIfFailed(
Polygon::CreateGeometry(m_deviceResources->GetD2DFactory(),
m_dstPolygons,
&m_pathGeometry)
);
// Update FPS display text
uint32 fps = timer.GetFramesPerSecond();
m_text = (fps > 0)? std::to_wstring(fps) + L" FPS" : L" - FPS";
}