Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Ask the ZMan: Applying Textures, Part 2

Part 2 of a three part series explaining the addition of textures to DirectX Primitive shapes.This column covers how to get a different texture on each side of the box.

Welcome to Ask The ZMan, a new column on the Coding4Fun game portal. The ZMan is here to solve your Managed DirectX programming problems. If you have a question for the ZMan send it to zman@thezbuffer.com

Source code:

Since Coding4Fun has only just launched, there aren't any specific questions to answer so I thought I would start with a question that occurs frequently on the Managed DirectX newsgroup:

"I've created my first Managed DirectX project and can successfully draw shapes created using the Mesh.{shape} primitives. However, when I try to apply a texture to them a) nothing happens b) the shapes go black or c) the shapes disappear. How can I add a texture to the DirectX primitive shapes?"

Last time I covered how texture coordinates are stored inside the mesh object, the relationship between the texture coordinates and the texture, and the most useful of the texture sampling states. In this column I will cover how to get a different texture on each side of the box.

A New Start Point

I left you last time with an exercise to add rotation into the crate texture viewer program. Nobody sent me their homework on time so I'm not sure if it was too easy or too hard.

Download the sample code using the link at the top of this article to see my solution (see figure 1).  You will need Beta2 of Visual C# Express and the April 2005 DirectX SDK. You can get Visual C# 2005 Express Edition Beta 2 by visiting http://msdn.microsoft.com/express. You can also use other Express Editions depending on the language of your choice, but this demonstration will utilize C#.

From the picture you can see I've added both rotation and translation. It is now possible to position the texture anywhere on the face of the box.


Figure 1. A solution to the exercise from the last column 

You will see that I have refactored the code to begin creating a reusable library. I would have preferred to create a new class inheriting from the DirectX mesh . However, the DirectX mesh class is sealed so this is not possible. I chose to create a static library which takes DirectX mesh objects as parameters.

Take a look at SetBoxTexture in TexturedMesh.cs and you will see some resemblance to the TextureMesh function from last time. To add in the rotation and translation I made the code a little more generic. I also parameterized the function rather than picking up the global variables.

First, I created a unit square which for the texture coordinates. Unchanged, these coordinates would cause the whole texture to cover each face.

C#

Vector2[] corners = new Vector2[4] 
{
    new Vector2(0f, 0f), //bottom left
    new Vector2(1f, 0f), //top left
    new Vector2(1f, 1f), //top right
    new Vector2(0f, 1f) //bottom right
};


Visual Basic
Dim corners As Vector2() = New Vector2() { _
    New Vector2(0.0!, 0.0!), 
    New Vector2(1.0!, 0.0!), 
    New Vector2(1.0!, 1.0!), 
    New Vector2(0.0!, 1.0!)}

Then I applied a transformation to each of these and stored the result into the texture coordinates of the mesh.

C#

//apply the transformation to each corner and store the result back 
//into the texture coordinates
for (int j=0; j<4; j++)
{
  corners[j].TransformCoordinate(transform);
  verts[i + j].Tu = corners[j].X;
  verts[i + j].Tv = corners[j].Y;
}


Visual Basic
For j As Integer = 0 To 3
    'apply the transformation to each corner and store the result back
    ' into the texture coordinates
    corners(j).TransformCoordinate(transform)
    verts(i + j).Tu = corners(j).X
    verts(i + j).Tv = corners(j).Y
Next

The transformation was created by concatenating together transformation matrices for each of the input parameters.

C#

Matrix transform =
Matrix.Translation(offsetX, offsetY, 0f)  
  * Matrix.Translation(-0.5f, -0.5f, 0f) //Translate the center of rotation
* Matrix.RotationZ(angle)               
  * Matrix.Scaling(1 / scaleX, 1 / scaleY, 1f) 
  * Matrix.Translation(.5f, .5f, 0f); //and move back the center of rotation


Visual Basic
Dim transform As Matrix = Matrix.Translation(offsetX, offsetY, 0.0F) * _
    Matrix.Translation(-0.5F, -0.5F, 0.0F) * Matrix.RotationZ(angle) * _
    Matrix.Scaling(1 / scaleX, 1 / scaleY, 1.0F) * _
    Matrix.Translation(0.5F, 0.5F, 0.0F)

I also added a TexturedMesh.Box() function which creates a mesh with texture coordinates already set to a useful default.

Displaying a Different Texture on Each Side

For boxes, it often makes sense to show the same texture on each side. But does this make sense for other shapes? Most of the meshes you encounter in a video game don't really have the same concept of sides that a box does and so it's worth finding out how to draw different things on different faces. Some of you may have heard that meshes can have multiple textures applied to them so it is theoretically possible to load up 6 textures, one for each face, and blend them together in some way. This technique is more often used to apply different kinds of textures to the whole object (for example, a base texture followed by a shadow map to darken some areas).

What I am going to describe is how to wrap a single texture around the box. Remember that the box is made up of 6 square faces, each of which is made up of 2 triangles. If you take these 6 squares and lay them side by side over a single texture it is possible to deduce the correct texture coordinates for each face.

You will eventually have many textures so it's important to make them as small as possible. This means leaving as little space between the squares as possible. DirectX also prefers textures to be as square as possible so the layout in figure 2 is optimal. This bitmap is what is normally called a texture map.


Firgure 2. A texture map template for a box

The larger typeface signifies which of the box faces applies to which part of the texture map. The smaller typeface is to give you an idea of the orientation of the texture and indicates which face touches that edge. The highlighted face is the example I will use to generate texture coordinates.

I have added a new overload to SetBoxTexture(). This one takes the mesh and a parameter called technique. Right now there is just one technique called Divide which means that the texture will be divided evenly between the 6 faces. Most of the function is similar to the other overloads. This version hard codes the texture coordinates for each face. There really is no easy mathematical way to do this. Remember texture coordinates go from 0.0 to 1.0 so the face texture coordinates had to be 0, 1/3, 2/3, 1.0 in the x direction and 0, 0.5, 1.0 in the y direction. I guessed which face was which and rendered the box. Once I saw where I was wrong I simply switched the indexes of the vertices around until the faces were in the correct place and had the correct orientation. This process took about 15 minutes to complete. Last time I mentioned how texture mapping was hard to do by hand. Imagine doing this for a more complex shape.

To set up the texture coordinates I simply assign the correct values to each vertex. There is a block of code similar to this for each face of the box. You should be able to match the vertex indices to those in figure 2 and confirm that the texture coordinates are correct.

C#

//Front
verts[20].Tu = 1f / 3f; verts[20].Tv = 1f;
verts[21].Tu = 1f / 3f; verts[21].Tv = 0.5f;
verts[22].Tu = 2f / 3f; verts[22].Tv = 0.5f;
verts[23].Tu = 2f / 3f; verts[23].Tv = 1f;


Visual Basic
'Front
verts(21).Tu = 1.0F / 3.0F
verts(21).Tv = 0.5F
verts(22).Tu = 2.0F / 3.0F
verts(22).Tv = 0.5F
verts(23).Tu = 2.0F / 3.0F
verts(23).Tv = 1.0F
verts(20).Tu = 1.0F / 3.0F
verts(20).Tv = 1.0F

To complete the render (see figure 3) a new mesh is created and its texture coordinates are set using the new function.

C#

MyBox = TexturedMesh.Box(e.Device, 2.0f, 2.0f, 2.0f);
TexturedMesh.SetBoxTexture(MyBox, TextureTechnique.Divide);


Visual Basic
MyBox = TheZBuffer.DirectX.Direct3D.TexturedMesh.Box(e.Device, 2.0F, 2.0F, 2.0F)
TheZBuffer.DirectX.Direct3D.TexturedMesh.SetBoxTexture(MyBox, _
    TheZBuffer.DirectX.Direct3D.TextureTechnique.Divide)

There are a couple of extra lines of code to load the template.bmp texture and ensure it is set before the mesh is rendered.


Figure 3 - Textured box showing the texture template

To make use of the template provided, load it up into your favorite image editor and replace the labels with graphics of your choice. Like most developers, I am not a great graphic artist so I chose to make a dice as an example since it only involves drawing circles. The final solution can be found by downloading the zmanTex04.msi file using the link at the top of this article. In the media folder you will find dice.bmp. If you load that texture instead of template.bmp you should see something like figure 4. 


Figure 4. Final render of the dice

Exercise for the Reader
You have seen what the texture map template for the box looks like. What do you think a texture map for a cylinder or a sphere would look like?

Future texturing questions coming soon:
How can  I texture the cylinder, sphere, torus, and teapot?

Credits:
Thanks to
Chris Zastrow (czastrow@newlogicmedia.com) for the crate textures found at http://www.planetquake.com/subverse/
Carlos Aguilar (web@carlosag.net) for the code colorizer found at http://www.carlosag.net/Tools/CodeColorizer/Default.aspx   

Copyright © 2005 TheZBuffer.com

Follow the Discussion

  • RapidRapid

    Hi Mr. Zman,

    I have a question that has bothered me for 2 weeks: how to render/rotate text using directx fast/efficiently? (e.g. render 'hello world' from (1, 0, 0) to (0, 0, 0)) I have tried the following but none of them is good:

    1. Use GDI+ to draw text on a bitmap and convert to texture

    2. Use a mesh from a string to render

    3. Use Surface.GetGraphics method to get GDI+ graphics to render

    4. Directly draw the rotated text using GDI+ after rendering directx stuff.

    5. Tried Sprite.Transform (not successfully and I do not believe it will work as I need)

    Could you please help me? Also, is there any simple way to detect collisions among texts in a 3D space? (Currently I have to convert the 3d coordinates of texts to screen coordinates and calculate rectangles or use ray trace for meshes. Very slow and ugly)

    Thanks a million and I am looking forward to hearing from you.

    Best Regards,

  • MarcosMarcos

    Hi.

    If I have one single mesh, with six subsets (each one is one side of the box), can I apply a different texture to each side? I would not like to use the same file, because my textures are separated, one per file.

    I've done a single test, like this:

    private void DrawMesh()

    {

       if (mesh != null)

       {

           for (int i = 0; i < mesh.NumberAttributes; i++)

           {

               if (textures[i] != null)

                   device.SetTexture(0, textures[i]);

               device.Material = materials[i];

               mesh.DrawSubset(i);

           }

       }

    }

    But all the SubSets are drawn with the same (the first) texture.

    Could you help me? Smiley

    Thanks.

  • VisitorVisitor

    1. Having to download and install just to go through and learn is stupid.

    2. Everything is custom or sub Classed. How are we supposed to learn when you can’t even see the form in the designer?

    3. Why convolute everything? Simplify things so we can get inspired and become creative and release that creativity by adding on to what you provide.

    4. If you’re going to make things complicated then clarify a masters in computer science or Microsoft certifications are required before following any of your post.

    I keep finding you on the search engines while looking for a solution to my problem and as always, there's a learning curve just to opening up your solutions. No answers, no solutions, just tons of custom crap you create! We get it; you’re great at what you do. Unfortunately that doesn’t do anything for us.

    Thanks for nothing!

  • Clint RutkasClint I'm a "developer"

    @Vistor, Video game development does have an extremely high learning curve.  Making games is hard.  There really aren't designers for them.  Typically you build out the designer and tools for them.  What ZMan is doing is giving you the theory behind it and showing you how to properly code it.

    3D development requires complex math.  Sadly, knowing how to do that type of math is semi required if you don't use a framework like XNA.  Even then it comes in handy.

    If you want to do video game development, check out http://creators.xna.com they have some very nice tutorials.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.