1 2 3 4 5 6 7
Setting up your Compiler Our Generic Windows Skeleton A Simple Cube Matrices Wazoo of Borg FullScreen Ahead Mr. Crusher On screen. Magnify.

 

 

 

Tutorial #5: Wazoo of Borg
Erik Yuzwa


Version History
Date Version Description Font Color
July 16th, 2001 1.0 General Public Release White
Sept. 10th, 2001 1.5 Updated code to reflect WIN32 skeleton changes Red

Resistance is futile mmmmmm Borg..


Well here we are at Direct3D tutorial #3..I've decided that since we already had a spinning cube, why not have some fun with it? So I tried my hand at texture mapping it with a Borg bmp I found on the internet...and you know what? It was EASY! Yes even for you beginners, DON'T PANIC. Texture mapping is not some arcane skill requiring five potions and a high save vs. magic skill. You can do it.

Texture Mapping? Imagine that you want to render a Borg Ship. Imagine all the pipes and steel traversing the ship. Now imagine trying to do that in a modelling program. Now imagine pulling your hair out in mindless frustration! Rather than take up hospital beds with modelers who go blind and crazy from doing a model such as the Borg ship, we can do what they call Texture Mapping. This is simply a process of mapping a 2D image onto a 3D primitive. This makes the primitive LOOK like ship/wall/floor or ANYTHING we want!

Inconceivable you say? Watch my friends, as we delve into the world of Texture Mapping..

We'll first create a new project, using the steps from our Tutorial #0

Okay we are going to be working from our basic Windows skeleton here so make sure you back up your original and only modify a copy..making it easier to reuse the skeleton in later applications..
We are going to declare some globals for our program at the top of your main program file
//in addition to our <windows.h> include file, add the rest
#include <mmsystem.h>// this handles our timing code
#include <d3dx8.h>// this is for our Direct3D8 object

#pragma comment(lib, "dxguid.lib")//This takes care of our GUID information
#pragma comment(lib, "d3dx8.lib")//This allows us to use the Direct3D8 utility functions
#pragma comment(lib, "d3d8.lib")//This allows us to use the Direct3D8 functions
#pragma comment(lib, "winmm.lib")//This allows us to use the timing library to spin our cube


//we are defining a NEW MYVERTEX structure to handle texture mapping coordinates
struct MYVERTEX
{
    FLOAT x, y, z;  // The vertex co-ordinates
    D3DCOLOR color; // The vertex color
    FLOAT tu, tv;   // our texture vertex co-ordinates
};

//our new flexible vertex format takes the texture mapping stuff into consideration
#define D3DFVF_MYVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

// application globals
LPDIRECT3D8             g_pD3D       = NULL;
LPDIRECT3DDEVICE8       g_pd3dDevice = NULL;
LPDIRECT3DINDEXBUFFER8  g_pCubeIB    = NULL;
LPDIRECT3DVERTEXBUFFER8 g_pCubeVB    = NULL;


LPDIRECT3DTEXTURE8 g_pBorgTexture = NULL;   //texture object to hold our texture


Okay nothing too tough there. Right away the astute of you will notice that we have defined a new flexible vertex format to reflect our texture mapping coordinates.
Note: Since Texture Maps are only 2D images, we can think of them as a grid from (0, 0) to (1,1)..except (0,0) is at the Upper Left, while (1,1) is at the Lower Right of our grid.
What we want to do is to map the texture onto our primitive.
////////////////////////////////////////////////////////
// GAME_INIT
// This module handles the initialization of our game
//
HRESULT Game_Init(HWND hWnd, HINSTANCE hInstance){

	//create our Direct3D object - IN ONE LINE!
	if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
		return E_FAIL;

	//get our default display mode from the Direct3D object
	D3DDISPLAYMODE d3ddm;
	if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
		return E_FAIL;

	
	//nothing's changed here...we still want a windowed application (don't worry
	//we'll do fullscreen soon...
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed   = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = d3ddm.Format;

	
	//create our Direct3D device using our D3DPRESENT_PARAMETERS parameters
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                  &d3dpp, &g_pd3dDevice ) ) )
		return E_FAIL;

	
    	// Turn off D3D lighting, since we are providing our own vertex colors
    	g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

	
	// we want to enable the solid fillmode for our primitives
	g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );

	
	//Now we will load our texture file of the Borg cube from ST:TNG..
	//we don't have to do much as there's another DirectX utility function
	//to help us. This will open the file, and transfer the image data into
	//our passed D3DTEXTURE8 object. I'll use it to load textures for now
	//until I notice a speed issue in the future...
	if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice,
					       "data/borg.bmp",
					       &g_pBorgTexture ) ) )
		return E_FAIL;

	
	//Okay I've had to change the way we create our cube vertex buffer, in order
	//to fully show how texture mapping works...(plus I had a hell of a time getting
	//the texture to map properly using the index buffer..heheeh).
	//I split up the vertices into logical groups of three, as we are rendering our
	//cube triangle by triangle. We are rendering ALL of the vertices in the EXACT
	//same order as we had our Index array in our first tutorial (do the numbers
	//after each vertex on the right look familiar??).

	//Although the only weird part is that the first vertex of each grouping
	//has the texture mapping information that really belongs in the PREVIOUS group
	//of 3 triangles...what I mean is that in order to draw a square, we only need
	//4 vertices. Since our primitive is a triangle, we are using two triangles to draw
	//each square. Make sense??

	//The other important thing to remember is that if you think of each face of your
	//primitive that you wish to render in 2D (for now), it may make the explanation
	//a bit easier. Since our textures only map in 2D, imagine placing the texture
	//onto the face of your square. We can specify how much of the texture map is
	//actually mapped on, as we have the range of 0.0 to 1.0 to work with. Don't
	//worry about halves for now...to map our complete image to each side, take
	//our first vertex (0,0,0). Now this is our bottom left corner of our square.
	//So we ALSO want the BOTTOM LEFT corner of our texture map, which is (0, 1)
	//Our next vertex (0,1,0), is our TOP LEFT corner of our square. The corresponding
	//TOP LEFT texture map coordinates are (0,0). Next vertex is our BOTTOM RIGHT
	//(Because we are drawing our square as 2 triangles), which means we also need
	//our BOTTOM RIGHT texture coordinates (1,1). Now we are done the first triangle.

	//the next vertex starts off our 2nd triangle which is STILL ON OUR FIRST SQUARE.
	//It is the bottom right of the square, so we need the appropriate texture
	//coordinate (1, 1). Our next vertex goes back up to the top left corner of the
	//same square. We already know this texture coordinates to be (0,0). FINALLY we
	//go to the LAST vertex of the square, (1,1,0), which is in the top left corner.
	//So our matching texture coordinate is (1,0).....done the front face! hehehe

	//the pattern repeats through the whole square...perhaps you should grab some
	//graph paper, and plot out these vertices there to better visualize what's
	//happening..

	//oh and I wanted to use a greyish color for the cube...
	MYVERTEX vCubeVertices[] =
	{
		//front face
		{0.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 1.0f}, //0
		{0.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //1
		{1.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //2

		{1.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //2
		{0.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //1
		{1.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 0.0f}, //3

		//right face
		{1.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 1.0f}, //2
		{1.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //3
		{1.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //4

		{1.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //4
		{1.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //3
		{1.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 0.0f}, //5

		//back face
		{1.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 1.0f}, //4
		{1.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //5
		{0.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //6

		{0.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //6
		{1.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //5
		{0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 0.0f}, //7

		//left face
		{0.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 1.0f}, //6
		{0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //7
		{0.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //0

		{0.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //0
		{0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //7
		{0.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 0.0f}, //1

		//top face
		{0.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 1.0f}, //1
		{0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //7
		{1.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //3

		{1.0f, 1.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //3
		{0.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //7
		{1.0f, 1.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 0.0f}, //5

		//bottom face
		{0.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 1.0f}, //0
		{1.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //2
		{0.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //6

		{1.0f, 0.0f, 0.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 1.0f}, //2
		{1.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 0.0f, 0.0f}, //4
		{0.0f, 0.0f, 1.0f, D3DCOLOR_RGBA(100, 100, 100, 255), 1.0f, 0.0f}  //6

	};

	if( FAILED( g_pd3dDevice->CreateVertexBuffer( 36 * sizeof(MYVERTEX), //allocate memory
                                              0, // Usage
					      D3DFVF_MYVERTEX, // format of MYVERTEX
                                              D3DPOOL_DEFAULT, // default D3DPOOL values
					      &g_pCubeVB ) ) ) // pointer to our Vertex Buffer
    	{return E_FAIL;}

	VOID* pVertices;
	if( FAILED( g_pCubeVB->Lock( 0, sizeof(vCubeVertices), (BYTE**)&pVertices, 0 ) ) )
    		{return E_FAIL;}

	memcpy( pVertices, vCubeVertices, sizeof(vCubeVertices) );
	g_pCubeVB->Unlock();


	return S_OK; //no error

}//end Game_Init
////////////////////////////////////////////////////////////////////////////////////

Ok, I can already hear the curses by my audience.."Wazoo you bastard! You removed the Index Buffer!"..yup! It was the only way I could think of to do texture mapping that showed you all what exactly was going on. But think of it this way, you know will know two methods of rendering your primitives: with an indexed array, and without.

So what I did for ease of readability is to group our vertices into groups of 3. I did this because it's a whole lot easier to read and mentally group our triangles together. There is a minor drawback to this in that you have to remember that 2 triangles make up each face of our cube.
I tried to explain how the texture mapping is setup in the comments above. If they didn't make any sense, PLEASE let me know. I'm not sure if I'm skipping over essential information, or if I'm going into TOO much detail..

Ok, next up is how we render our Borg cube to the screen. Much like OpenGL, Direct3D functions as a giant state machine. So all we have to do is ENABLE our texture mapping, and the geometry pipeline will AUTOMATICALLY use it, until we DISABLE the texture mapping. Because we're only drawing one cube however, I didn't bother to worry about DISABLING anything for now..
////////////////////////////////////////////////////////
// GAME_RUN
// This module handles the main loop of our game
//
void Game_Run(void *params){

	// This function is responsible for updating each frame

	// For our world matrix, we will just rotate the object about the y-axis.
    	D3DXMATRIX matWorld;
	D3DXMatrixRotationY( &matWorld, timeGetTime()/300.0f );
	g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

    	// Set up our view matrix. A view matrix can be defined given an eye point,
    	// a point to lookat, and a direction for which way is up. Here, we set the
    	// eye five units back along the z-axis and up three units, look at the
    	// origin, and define "up" to be in the y-direction.
    	D3DXMATRIX matView;
    	D3DXMatrixLookAtLH( &matView,
			    &D3DXVECTOR3( 0.0f, 3.0f, -5.0f ),
			    &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
			    &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );

	g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

    	// For the projection matrix, we set up a perspective transform (which
    	// transforms geometry from 3D view space to 2D viewport space, with
    	// a perspective divide making objects smaller in the distance). To build
    	// a perpsective transform, we need the field of view (1/4 pi is common),
    	// the aspect ratio, and the near and far clipping planes (which define at
    	// what distances geometry should be no longer be rendered).
    	D3DXMATRIX matProj;
    	D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );
    	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

	
	/// This function is responsible for rendering our frame to the screen
	// Clear the back buffer to a blue color
	g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );

	// Begin the scene.
	g_pd3dDevice->BeginScene();

	//let the Direct3d Device know which texture we want...
	g_pd3dDevice->SetTexture(0, g_pBorgTexture);

	
	//There's SEVERAL texture stages, which we won't worry about for now. We're
	//not going to do any multi-texturing or anything like that for a while, so
	//be nieve and pretend there's only 1 texture stage (there can only be ONE!)

	//COLOROP, this is the texture color blending argument for this stage
	//COLORARG1, this is the first color argument for this stage
	//COLORARG2, this is the second color argument for this stage
	//ALPHAOP is disabled as we're not doing any alpha blending for the moment
	g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
	g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
	g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );

	// Rendering of scene objects happens here.
	g_pd3dDevice->SetVertexShader( D3DFVF_MYVERTEX );
	g_pd3dDevice->SetStreamSource( 0, g_pCubeVB, sizeof(MYVERTEX) );

	
	// this is where we go through the vertex buffer, processing the information
	// that we've put into the Index Buffer.
	// First Param - our primitive drawing style
	// Second Param - our starting vertex in our vertex buffer
	// Third Param - our number of triangles to draw
	//
	g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 12);

	
	// End the scene.
	g_pd3dDevice->EndScene();

	
	//blast everything to the screen...replaces the DDraw->Flip command
	g_pd3dDevice->Present( NULL, NULL, NULL, NULL );


}//end Game_Frame
//////////////////////////////////////////////////////////////////////////////////

Okay no problem we get Texture Mapping right?? hehehe. So here all we did was show how to draw a texture mapped cube...The BORG! I know I glossed over the Texture Stage stuff pretty quick, but for right now I didn't want to focus on it, and lose a lot of readers. After all, this is our 3rd Tutorial! I'll wait until later to lose you..hehehe. Seriously, it is important information and feel free to search out your DirectX8 docs to explain more of it if you need to...or depending on your feedback, I'll flesh it out more. I just didn't want to get bogged down and risk confusing the hell out of everyone.

Now we'll move on to the cleanup code, which is the same except I took out the stuff dealing with our now deceased Index Buffer
////////////////////////////////////////////////////////
// GAME_SHUTDOWN
// This module handles the shutdown and cleanup of our game
//
void Game_Shutdown(void *params) {

	if( g_pBorgTexture ){
		g_pBorgTexture->Release();
		g_pBorgTexture = NULL;
	}

	if( g_pCubeVB ){
		g_pCubeVB->Release();
		g_pCubeVB = NULL;
	}

	if( g_pd3dDevice ) {
        g_pd3dDevice->Release();
		g_pd3dDevice = NULL;
	}

	if( g_pD3D ) {
        g_pD3D->Release();
		g_pD3D = NULL;
	}

}//end Game_Shutdown
////////////////////////////////////////////////////////

Well the only thing I changed in the Game_Shutdown code was the cleaning up of the index buffer that we used back in Tutorial #1. Nothing special here.

Well my friends, that was your first introduction to the world of Texture Mapping with Direct3D. That wasn't TOO bad was it? I'm hoping that I was able to teach you the basics of Texture Mapping, at least well enough to give you a small understanding of what's happening..


Back To Index

 

 


 

[top]

 
 
© 2003, Silver.
Wind, two Tears, and a Funeral...