xboxscene.org forums

Pages: [1] 2

Author Topic: The Ultimate Xdk Tutorial  (Read 303 times)

predatorkill

  • Archived User
  • Newbie
  • *
  • Posts: 5
The Ultimate Xdk Tutorial
« on: March 10, 2003, 02:59:00 AM »

Prt1 - "Empty Code" - is that it?
So you've got the XDK, and you want a little tasty of how it works, how to get your little fingers in there.
Well remember once you've installed the XDK, you start up visual C++, and you want to know where to start...this takes us the the entry point...the place where it all begins.

//Main header file for the XDK
#include <xtl.h>

//Application entry point
void __cdecl main()
{

     StartGame();
     
}

What did you say? .."My god what is that!", well this is the entry point for all our programs, I've put a function called StartGame() in the body of the main loop which will call our code to start the game loop.
You could take the function call out "StartGame()" and press compile and it could compile and generate a .xbe for you which would work on the xbox.
Note:  If you are using a mod-chip, and evo-x and you wish to run your .xbe on the xbox you must first patch it, copy it across using an ftp package such as FlashFXP then using FlashFXP send the execute binary command and away it goes.

Prt2 - "DirectX Initilisation" - a blue screen
Well you can't go through life just adding in a few lines of code that do nothing - so now for some juicy code you can look at.  Don't be scared, this code we learn once, put it in an init() function and we can just call it whenever we start using directX.
The code more or less stays the same.  From now on you'll be able to cut and copy this code into further projects, or as I usually do, put it in a init.cpp file.

//Main header file for the XDK
#include <xtl.h>


//Application entry point
void __cdecl main()
{
     InitialiseD3D();
     while(true)
     {
           //Clear the backbuffer to black
               //                                                      r   g   b
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,  0, 255), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();


           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
     }    

     void CleanUp();
}
DirectX Initialisation code, which usually remains the same.  i.e.  you could put it in a separate file and just add it to each new project you create.
LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   //First of all, create the main D3D object. If it is created successfully we
   //should get a pointer to an IDirect3D8 interface.
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

   //Create a structure to hold the settings for our device
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));

   //Fill the structure.
   // Set fullscreen 640x480x32 mode

     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;


     // Create one backbuffer
     d3dpp.BackBufferCount = 1;



     // Set up how the backbuffer is "presented" to the frontbuffer each time
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

   //Create a Direct3D device.
   g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);
}

void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}
So what happens - well when you run this little snippet of code don't expect to much.  It just shows you the pieces that make up directX.  It initilises directX then renders the screen (e.g. clears it blue).  A blue screen is all you get when you run this little snippet of code.
Well thats it, you should be able to look at this code and understand it.  I've not details a lot of the small stuff, because I'll come back and do it later.  For example - in g_pD3D->CreateDevice(0, D3DDEVICE_HAL, NULL, .., .., ..).  The D3DDEVICE_HAL informs directX to use hardware for the computations.



Prt3 - Wow a triangle
Finally something exciting - well if you understand it all up to now, you'll be a game programmer in a few days.  3D is based on vertices, e.g. points in space.  X,Y,Z value represents were in space it is located.  We just join up these points to create complex shapes, like those used in Doom, or Halo :)
A lot of the initialisation code is the same as before, you'll get used to it in time, and eventually just put it in a separate file where you can call it just once.
//Application entry point
void __cdecl main()
{
     InitialiseD3D();
     while(true)
     {
           //Clear the backbuffer to black
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();


                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 DrawTriangle();

           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
     }    

     void CleanUp();
}
And next comes the DirectX Initilisation, and De-Initilisation.
//Main header file for the XDK
#include <xtl.h>

LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   //First of all, create the main D3D object. If it is created successfully we
   //should get a pointer to an IDirect3D8 interface.
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

   //Create a structure to hold the settings for our device
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));

   //Fill the structure.
   // Set fullscreen 640x480x32 mode

     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;


     // Create one backbuffer
     d3dpp.BackBufferCount = 1;

     // Set up how the backbuffer is "presented" to the frontbuffer each time
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

   //Create a Direct3D device.
   g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);
}

void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}
Finally the Code that draws our wonderful, wonderful triangle for us.  As you can see, the DrawTriangle code has to be called within the ->BeginScene(), and                      ->EndScene() member functions above in main(), I've commented the code with //NEW NEW.. so that you can see the main points I am trying to outline.
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

LPDIRECT3DVERTEXBUFFER8 g_pVertexBuffer = NULL; // Vertices Buffer

struct CUSTOMVERTEX
{
   FLOAT x, y, z, rhw; // The transformed position for the vertex.
   DWORD colour; // The vertex colour.
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)


void DrawTriangle()
{
   VOID* pVertices;
   
   //Store each point of the triangle together with it's colour
   CUSTOMVERTEX cvVertices[] =
   {
       {250.0f, 100.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(255, 0, 0),}, //Vertex 1 - Red (250, 100)
       {400.0f, 350.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(0, 255, 0),}, //Vertex 2 - Green (400, 350)
       {100.0f, 350.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(0, 0, 255),}, //Vertex 3 - Blue (100, 350)
   };

   //Create the vertex buffer from our device
   g_pD3DDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX),
                                              0,
                                                                    D3DFVF_CUSTOMVERTEX,
                                              D3DPOOL_DEFAULT,
                                                                    &g_pVertexBuffer);
   //Get a pointer to the vertex buffer vertices and lock the vertex buffer
   g_pVertexBuffer->Lock(0, sizeof(cvVertices), (BYTE**)&pVertices, 0);

   //Copy our stored vertices values into the vertex buffer
   memcpy(pVertices, cvVertices, sizeof(cvVertices));

   //Unlock the vertex buffer
   g_pVertexBuffer->Unlock();

     //Rendering our triangle
   g_pD3DDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(CUSTOMVERTEX));
   g_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
   g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
}

//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
Yup, I can hear you say it from here, "Wow", a triangle, not just any triangle, a funky coloured textured one on a blue background.  Well when you've just started out in the big bad world of xdk development I think it seems pretty rewarding.
You can see from the DrawTriangle() function, that we set some vertices up (e.g. x,y,z points) and just copy them into our directX buffer and render them to the screen.  DirectX buffer?  Whats that?  Well your graphics card has memory onboard, and if we use the directX buffer it will put it in there so that we can obtain better performance.


Prt4 - Textured Triangle, I'm scared...
Well the codes not that much different than a simple coloured triangle, just added a couple of new lines, ... now the codes starting to get long and if you understand it good, if not, sit down with a good cup of coffee and go over it a few times.  These are the basics that all the other examples will work on.
//Application entry point
void __cdecl main()
{
     InitialiseD3D();
     while(true)
     {
           //Clear the backbuffer to black
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();


                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 DrawTexturedTriangle();

           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
     }    

     void CleanUp();
}
Well as I promised practically anything has changed in the main() function, anything new has been commented with "//NEW NEW" at the start and end.
//Main header file for the XDK
#include <xtl.h>

LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   //First of all, create the main D3D object. If it is created successfully we
   //should get a pointer to an IDirect3D8 interface.
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

   //Create a structure to hold the settings for our device
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));

   //Fill the structure.
   // Set fullscreen 640x480x32 mode

     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;


     // Create one backbuffer and a zbuffer
     d3dpp.BackBufferCount = 1;
     d3dpp.EnableAutoDepthStencil = TRUE;
     d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;



     // Set up how the backbuffer is "presented" to the frontbuffer each time
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

     //Create a Direct3D device.
     g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);


     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
     g_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
     g_pD3DDevice->SetTextureStageState(0,D3DTSS_COLORARG1, D3DTA_TEXTURE);

     //Turn off lighting becuase we are specifying that our vertices have textures colour
     g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

}



void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}
Well below shows the juicy part of the code which actually makes the drawing of a textured triangle possible.  I can see the excitement in your eyes - just think of the possibilities.
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

LPDIRECT3DVERTEXBUFFER8 g_pVertexBuffer = NULL; // Vertices Buffer
LPDIRECT3DTEXTURE8 pTexture = NULL;

struct CUSTOMVERTEX
{
   FLOAT x, y, z; // The transformed position for the vertex.
   DWORD colour; // The vertex colour.
   FLOAT tu, tv;
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)


void DrawTexturedTriangle()
{
   VOID* pVertices;
   
   //Store each point of the triangle together with it's colour
   CUSTOMVERTEX cvVertices[] =
   {
       { -1.0f, -1.0f, 0.0f, 0x00FF0000, 0.0f, 1.0f }, // x, y, z, color
           { -1.0f,  1.0f, 0.0f, 0x0000FF00, 0.0f, 0.0f },
           {  1.0f,  1.0f, 0.0f, 0x000000FF, 1.0f, 0.0f }
   };
     
     //FileName is "D:\xfactordev.bmp"
     D3DXCreateTextureFromFile(g_pD3DDevice, "D:\xfactordev.bmp", &pTexture);

   //Create the vertex buffer from our device
   g_pD3DDevice->CreateVertexBuffer(3 * sizeof(CUSTOMVERTEX),
                                              0,
                                                                    D3DFVF_CUSTOMVERTEX,
                                              D3DPOOL_DEFAULT,
                                                                    &g_pVertexBuffer);

   //Get a pointer to the vertex buffer vertices and lock the vertex buffer
   g_pVertexBuffer->Lock(0, sizeof(cvVertices), (BYTE**)&pVertices, 0);

   //Copy our stored vertices values into the vertex buffer
   memcpy(pVertices, cvVertices, sizeof(cvVertices));

   //Unlock the vertex buffer
   g_pVertexBuffer->Unlock();

     //Rendering our triangle
   g_pD3DDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(CUSTOMVERTEX));
   g_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
   //Set our background to use our texture buffer
   g_pD3DDevice->SetTexture(0, pTexture);
   g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);

     g_pVertexBuffer->Release();
     pTexture->Release();


}

//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
An important thing not to forget when testing this code, don't forget to put the bitmap (e.g. xfactordev.bmp) in the same folder that your running the .xbe from.  Or else it will just crash your xbox and you'll have to re-start it.
Its a simple piece of code, the only real difference between this piece of code and the code from earlier (e.g. a basic triangle) is that we have a DirectX texture buffer, and have added texture coordinates to our CUSTOMVERTEX structure.
When you run this code, you'll get half a triangle pasted across your screen which is blue, and on the other half, you'll get a textured triangle with the contents of the bitmap stretched onto it.
I can see you drooling with excitement....Oooo...yeah...just think, where getting closer and closer to that polygon drawn character with a texture mapped onto him.


Prt5 - "Hello World"
Well every new programmer knows of hello world - its tradition - you can't go against tradition :)  So when you compile this little piece of code and run it, you'll get a small piece of text (green text) in top left of the screen saying "Hello World"..
So to start off, the initilisation, as before, nothing much has changed.  The only difference is the added header files "#include <xfont.h>, #include <stdio.h>".
//Main header file for the XDK
#include <xtl.h>

//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
#include <xfont.h> //used for directX textout
#include <stdio.h> //used for swprintf
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   //First of all, create the main D3D object. If it is created successfully we
   //should get a pointer to an IDirect3D8 interface.
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);

   //Create a structure to hold the settings for our device
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));

   //Fill the structure.
   // Set fullscreen 640x480x32 mode

     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;


     // Create one backbuffer and a zbuffer
     d3dpp.BackBufferCount = 1;

     // Set up how the backbuffer is "presented" to the frontbuffer each time
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

   //Create a Direct3D device.
   g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);

   g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
}



void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}
And for the main part of the program, the entry point - we have:
//Application entry point
void __cdecl main()
{
     InitialiseD3D();
     while(true)
     {
           //Clear the backbuffer to black
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();


                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 DisplayText();

           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
     }    
     void CleanUp();
}
Now for the part of code which works its magic, and loads in the bitmap font from file, and displays our wonderful wonderful exciting text :)
void DisplayText()
{
     //Create some DirectX text buffers
     XFONT*      m_pArial18BitmapFont;    // Pointer to the Arial18Normal Bitmap font    
     LPDIRECT3DSURFACE8 g_pFrontBuffer;
 
   //InitialiseFonts
   g_pD3DDevice->GetBackBuffer(-1,D3DBACKBUFFER_TYPE_MONO,&g_pFrontBuffer);
     DWORD dwFontCacheSize = 16 * 1024;

     //Load our font in - have to sepcify its loacation
     XFONT_OpenBitmapFont( L"D:\Arial18Normal.bmf",
                          dwFontCacheSize, &m_pArial18BitmapFont );

     WCHAR szbuff[200] = {0};
     swprintf(szbuff, L"Hello World");
     //Top left corner of where we want to draw our text.
     float xpos = 100.0f;
     float ypos = 100.0f;

     //Display our text.
     m_pArial18BitmapFont->SetTextColor(D3DCOLOR_XRGB(30,255,20));

     m_pArial18BitmapFont->TextOut( g_pFrontBuffer, szbuff, -1, (long)xpos, (long)ypos );

     //Release our TextBuffers
     m_pArial18BitmapFont->Release();
     g_pFrontBuffer->Release();

}
I didn't write this piece of code for optimisation!  Every time you call it, it is creating and destroying the font buffer.  But it is exceptionally easy to understand I think, that is why I wrote it this way.  You should be able to understand the jist of what is happening.  In time this will be made into a class, or the XFONT* buffer could be put in the directX initilisation code.
Don't forget to put the file "Arial18Normal.bmf" in your directory with the .xbe, or else it won't do anything, and will probably just crash.



Prt6 - Don't be scared it won't bite - Sphere "AKA Ball"
Well you can still turn back.... its going to get a little complex for a few minutes. I wanted to show you how to generate shapes, like 3D packages do. Give you a taste of some real sticky code :)
But don't worry I'll take you throught it hand by hand and slowly. I've tried to rip as much of the un-necessary stuff out, and keep it in a nice easy to swollow function.
I think if you sit down with a jar of coffee and a spoon and read through it you might ....just maybe...hopefully...grasp it :>
Two parts? Yup, I split it into two parts.
Part1. Will give you a basic sphere, the simplest possible that uses a "TRIANGLESTRIP" to draw the sphere. This is basically instead of generating a load of seperate triangles to represent all the little triangles that make up our cool shape, we just generate a load of vertex points one after the other and we can use that.
Part2. I used "TRIANGLELIST" - its a little more complex.... hec is a lot more complex... instead I modified the code a bit and did it so the sphere is now made up of loads of seperate triangles. The vertex buffer holds a complet list of seperate triangles...e.g. the first three vertex points make up one triangle on the sphere, the next three points the next triangle etc.
Why did I do this? Well I must a reason, later on I'll show you how to make the sphere explode outwards, e.g. all the sides of the sphere will go opposite way so you get an explosion sort of effect...which will be cool :)


Prt6a - Sphere "AKA Ball"
Squares, and cubes and other small shapes, this tutorial will give you a feel of how shapes can be generated using algorithms.  I don't know if you've used such packages as 3D Studio Max and Maya or even MilkShape - but these packages allow you to draw 3D shapes in your scene, specify there width, there complexity etc.  These are generated as I'll show you now.
Not before you even think of looking at this code, I'd go and grab 4 or 5 cups of coffee....its a scary beast... once you grasp it though you'll be using a it a lot in the future for cool effects in your games.  You could represent bullets etc using the sphere..
Now where going in, hold on tight, and don't stick your arms out......
This is it!...The magic function.... of course its not optimised or anything, but you could copy and paste this into any project and....tadaaa...a sphere would be born on your screen.
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

void DrawSphere()
{
     UINT D3DFVF_CUSTOMVERTEX =  (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE);
     struct CUSTOMVERTEX
     {
           FLOAT x, y, z;                //Vertex position
           FLOAT nx, ny, nz;       //Direction the vertex is facing(normal)
           DWORD colour;                 //The vertex colour.
     };

     //These two variables determine the quality of our sphere, try changing them,
     //if you put a large number in them you'll get a very details sphere...made
     //up of very small triangles.
     int nRings        = 15;
     int nSegments     = 12;

     DWORD dwNumOfVertices   = (nRings + 1) * (nSegments + 1);
     DWORD dwNumOfIndices    = 2 * nRings * (nSegments + 1);

     LPDIRECT3DVERTEXBUFFER8 pVertexBuffer     = NULL;
     IDirect3DIndexBuffer8* pIndexBuffer       = NULL;

     g_pD3DDevice->CreateVertexBuffer(dwNumOfVertices * sizeof(CUSTOMVERTEX),
                                                                 0, D3DFVF_CUSTOMVERTEX,
                                                                 D3DPOOL_DEFAULT, &pVertexBuffer);

     g_pD3DDevice->CreateIndexBuffer(dwNumOfIndices * sizeof(WORD),
                                                                 0, D3DFMT_INDEX16, D3DPOOL_MANAGED,
                                                                 &pIndexBuffer);

     WORD *pIndices;
     CUSTOMVERTEX *pVertex;

     pVertexBuffer->Lock(0,0, (BYTE**)&pVertex, 0);
     pIndexBuffer->Lock(0,dwNumOfIndices, (BYTE**)&pIndices, 0);

     WORD wVertexIndex = 0;
     D3DXVECTOR3 vNormal;

     //Setup some angles
     float rDeltaRingAngle   = (D3DX_PI / nRings);
     float rDeltaSegAngle    = (2.0f * D3DX_PI / nSegments);

     float red = 0.0f, green = 1.0f, blue = 0.0f;
     //Generate the group of rings for the sphere
     for(int nCurrentRing = 0; nCurrentRing < nRings + 1; nCurrentRing++)
     {
           float r0 = sinf(nCurrentRing * rDeltaRingAngle);
           float y0 = cosf(nCurrentRing * rDeltaRingAngle);

           //OOooo Gerneate the group of segments for the current ring
           for(int nCurrentSegment=0; nCurrentSegment < nSegments + 1; nCurrentSegment++)
           {
                 float x0 = r0 * sinf(nCurrentSegment * rDeltaSegAngle);
                 float z0 = r0 * cosf(nCurrentSegment * rDeltaSegAngle);

                 vNormal.x = x0;
                 vNormal.y = y0;
                 vNormal.z = z0;

                 D3DXVec3Normalize(&vNormal, &vNormal);

                 //Add one vector to the strip which makes up the sphere
                 pVertex->x  = x0;
                 pVertex->y  = y0;
                 pVertex->z  = z0;
                 pVertex->nx = vNormal.x;
                 pVertex->ny = vNormal.y;
                 pVertex->nz = vNormal.z;

                 pVertex->colour = D3DXCOLOR(red, green, blue, 1.0f);
                 red += 0.02f;
                 blue += 0.01f;
                 green -= 0.015f;
                 //You could set all the vertices the same colour, but to add a some different colours
                 //I'll add a variable that changes
                 //pVertex->colour = 0xff00ff00;

                 /*
                 //Alternatively you could set texture coordinates e.g:
                 pVertex->tu = 1.0f - ( (float)nCurrentSegment / (float)nSegments );
                 pVertex->tv = (float)nCurrent / (float)nRings;
                 */
                 
                 pVertex++;

                 //Add two indices except for the last ring
                 if(nCurrentRing != nRings)
                 {
                       *pIndices = wVertexIndex;
                       pIndices++;

                       *pIndices = wVertexIndex + (WORD)(nSegments + 1);
                       pIndices++;

                       wVertexIndex++;
                 }
           }
     }

     pIndexBuffer->Unlock();
     pVertexBuffer->Unlock();

     g_pD3DDevice->SetStreamSource(0, pVertexBuffer, sizeof(CUSTOMVERTEX));
     g_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
     //Select the index buffer
     g_pD3DDevice->SetIndices(pIndexBuffer, 0);

     DWORD dwNumOfPolygons = dwNumOfIndices - 2;
     //Render the polygons from the index buffer
   //Note ~ That you can change D3DPT_LINESTRIP to D3DPT_TRIANGLESTRIP to see the
     //coloured in - by using the linestrip we see the sphere as a mesh.
     g_pD3DDevice->DrawIndexedPrimitive(D3DPT_LINESTRIP, 0, dwNumOfVertices, 0,
                                                                 dwNumOfPolygons);

     pIndexBuffer->Release();
     pVertexBuffer->Release();
}

//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

Well there's other parts of the code .... I added a little bit of matrix's in part 2, so make sure you see the differences in this and the next part....itsso that you could see the sphere rotate - matrixes are scares...even to me.. which is why later I promise a tutorial on them.  ABC to Matrices at a screen near you soon.
//Main header file for the XDK
#include <xtl.h>

LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));
     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
     d3dpp.BackBufferCount = 1;
     d3dpp.EnableAutoDepthStencil = TRUE;
     d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
   g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);

     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
     g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
     //Turn on z-buffering
     g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
     //Turn off culling - so we can see the back of the sphere :)
     g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
}

void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}

And finally our entry point.  Now if you look carefully you'll notice a few new lines... don't be scared.... all these do is put a camera in the picture.  Well originally we where set in a fixed position, e.g. at 0,0,0 looking into the screen.  With these few lines of code we can look anywhere we want... up, down ... we can even write it so that if we move our game pad the camera moves and we can go walking around our 3D world.  Of course all thats there at the moment is a sphere....but one day...it will be more.
//Application entry point
void __cdecl main()
{
     InitialiseD3D();


     while(true)
     {
           //Clear the backbuffer to black
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();

                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 //camera
                 D3DXMATRIX view_matrix, matProj;
                 D3DXMatrixLookAtLH(&view_matrix,&D3DXVECTOR3( 0.0f, 0.0f,-5.0f ),
                                  &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                                  &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ));
                 g_pD3DDevice->SetTransform(D3DTS_VIEW,&view_matrix);
                 D3DXMatrixPerspectiveFovLH(&matProj, //Result Matrix
                             D3DX_PI/4,//Field of View, in radians. (PI/4) is typical
                             ((float)600 / (float)400),     //Aspect ratio
                             1.0f,     //Near view plane
                             1000.0f ); // Far view plane
                 g_pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj );
                 //end camera



                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 DrawSphere();

           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
     }    

     void CleanUp();
}



Prt6b - More than just a shape - a rotating shape...Cool
To take the sphere to a higher level, I've added some complex stuff which I'm wondering if I should have .... I've added a light, so that our sphere is illuminated - put those normals to good use.  Also I've added a matrix to rotate our world.... its a lot of code.... and its messy... but still ... this piece of code will be a cool in the future when we make it explode....
Now before you go running away screaming...take a deep breadth....that's it...now stay calm...its not that bad ...
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

void DrawSphereTriangleList()
{
     UINT D3DFVF_CUSTOMVERTEX =  (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE);
     struct CUSTOMVERTEX
     {
           FLOAT x, y, z;
           FLOAT nx, ny, nz;
           DWORD colour;
     };

     int nRings = 4;
     int nSegments = 4;

     DWORD dwNumOfVertices = (nRings + 1) * (nSegments + 1);    

     DWORD dwNumOfPolygons = dwNumOfVertices * 3;

     LPDIRECT3DVERTEXBUFFER8 pVertexBuffer = NULL;

     //CUSTOMVERTEX pVertex[dwNumOfVertices*3];
     CUSTOMVERTEX pVertex[25*3] = {0};

     
     D3DXVECTOR3 vNormal;

     int nCurrentRing = 0;

     //Setup some angles
     float rDeltaRingAngle   = (D3DX_PI / nRings);
     float rDeltaSegAngle    = (2.0f * D3DX_PI / nSegments);

     //PART-1- Read in all the vertices that make up the shape
     //Generate the group of rings for the sphere
     for(nCurrentRing = 0; nCurrentRing < nRings +1; nCurrentRing++)
     {
           float r0 = sinf(nCurrentRing * rDeltaRingAngle);
           float y0 = cosf(nCurrentRing * rDeltaRingAngle);

           
           //OOooo Generate the group of segments for the current ring
           for(int nCurrentSegment=0; nCurrentSegment < nSegments + 1; nCurrentSegment++)
           {
                 float x0 = r0 * sinf(nCurrentSegment * rDeltaSegAngle);
                 float z0 = r0 * cosf(nCurrentSegment * rDeltaSegAngle);

                 vNormal.x = x0;
                 vNormal.y = y0;
                 vNormal.z = z0;

                 D3DXVec3Normalize(&vNormal, &vNormal);

                 //Add one vector to the strip which makes up the sphere
                 int i = nCurrentRing * nSegments + nCurrentSegment;

                 pVertex.x = x0;
                 pVertex.y = y0;
                 pVertex.z = z0;
                 pVertex.nx = vNormal.x;
                 pVertex.ny = vNormal.y;
                 pVertex.nz = vNormal.z;
                 
                 pVertex.colour = 0xff00ff00;

           }
     }
     //PART-2- Arrange them in list order
     //This part puts all of our lovely vertices into a nice tidy order of triangles.
     //CUSTOMVERTEX pVertexList[dwNumOfVertices*3];
     CUSTOMVERTEX pVertexList[50*3] = {0};

     int index = 0;
     for(nCurrentRing = 0; nCurrentRing < nRings +1; nCurrentRing++)
     {
           for(int nCurrentSegment=0; nCurrentSegment < nSegments + 1; nCurrentSegment++)
           {
                 if(nCurrentRing != nRings)
                 {
                       //These few lines specify the triangles into the pVertexList, as
                       //above we have just generated the vertices for the sphere, but they
                       //arnt aranged in an order of triangles, e.g. the first three points
                       //make up a triangle, the next three points make up a second triangle
                       //etc.  These lines within the loop take the points and generate
                       //a list of triangles which we can draw using D3DPT_TRIANGLELIST

                       int i =  nCurrentRing * nSegments;

                       pVertexList[index].x = pVertex[i+nCurrentSegment].x;
                       pVertexList[index].y = pVertex[i+nCurrentSegment].y;
                       pVertexList[index].z = pVertex[i+nCurrentSegment].z;

                       pVertexList[index].nx = pVertex[i+nCurrentSegment].nx;
                       pVertexList[index].ny = pVertex[i+nCurrentSegment].ny;
                       pVertexList[ index].nz = pVertex[i+nCurrentSegment].nz;

                       pVertexList[index].colour = pVertex[i+nCurrentSegment].colour;
                       
                       index++;
                       ///////////////////////////////////////////////////////////////
                       pVertexList[index].x = pVertex[i+nCurrentSegment + nSegments].x;
                       pVertexList[index].y = pVertex[i+nCurrentSegment + nSegments].y;
                       pVertexList[index].z = pVertex[i+nCurrentSegment + nSegments].z;

                       pVertexList[index].nx = pVertex[i+nCurrentSegment + nSegments].nx;
                       pVertexList[index].ny = pVertex[i+nCurrentSegment + nSegments].ny;
                       pVertexList[index].nz = pVertex[i+nCurrentSegment + nSegments].nz;

                       pVertexList[index].colour = pVertex[i+nCurrentSegment + nSegments].colour;

                       index++;
                       //////////////////////////////////////////////////////////////

                       pVertexList[index].x = pVertex[i+nCurrentSegment +1].x;
                       pVertexList[index].y = pVertex[i+nCurrentSegment  +1].y;
                       pVertexList[index].z = pVertex[i+nCurrentSegment  +1].z;

                       pVertexList[index].nx = pVertex[i+nCurrentSegment  +1].nx;
                       pVertexList[index].ny = pVertex[i+nCurrentSegment  +1].ny;
                       pVertexList[index].nz = pVertex[i+nCurrentSegment  +1].nz;

                       pVertexList[index].colour = pVertex[i+nCurrentSegment+1].colour;

                       index++;

                       /////////////////////////////////////////////////////////////
                       /////////////////////////////////////////////////////////////
                       /////////////////////////////////////////////////////////////
                       
                       pVertexList[index].x = pVertex[i+nCurrentSegment+ nSegments].x;
                       pVertexList[index].y = pVertex[i+nCurrentSegment+ nSegments].y;
                       pVertexList[index].z = pVertex[i+nCurrentSegment+ nSegments].z;

                       pVertexList[index].nx = pVertex[i+nCurrentSegment+ nSegments].nx;
                       pVertexList[index].ny = pVertex[i+nCurrentSegment+ nSegments].ny;
                       pVertexList[ index].nz = pVertex[i+nCurrentSegment+ nSegments].nz;

                       pVertexList[index].colour = pVertex[i+nCurrentSegment+ nSegments].colour;
                       
                       index++;
                       ///////////////////////////////////////////////////////////////
                       pVertexList[index].x = pVertex[i+nCurrentSegment + nSegments+ 1].x;
                       pVertexList[index].y = pVertex[i+nCurrentSegment + nSegments+ 1].y;
                       pVertexList[index].z = pVertex[i+nCurrentSegment + nSegments+ 1].z;

                       pVertexList[index].nx = pVertex[i+nCurrentSegment + nSegments+ 1].nx;
                       pVertexList[index].ny = pVertex[i+nCurrentSegment + nSegments+ 1].ny;
                       pVertexList[index].nz = pVertex[i+nCurrentSegment + nSegments+ 1].nz;

                       pVertexList[index].colour = pVertex[i+nCurrentSegment + nSegments].colour;

                       index++;
                       //////////////////////////////////////////////////////////////

                       pVertexList[index].x = pVertex[i+nCurrentSegment +1].x;
                       pVertexList[index].y = pVertex[i+nCurrentSegment  +1].y;
                       pVertexList[index].z = pVertex[i+nCurrentSegment  +1].z;

                       pVertexList[index].nx = pVertex[i+nCurrentSegment  +1].nx;
                       pVertexList[index].ny = pVertex[i+nCurrentSegment  +1].ny;
                       pVertexList[index].nz = pVertex[i+nCurrentSegment  +1].nz;

                       pVertexList[index].colour = pVertex[i+nCurrentSegment+1].colour;

                       index++;

                 }
           }

     }


     g_pD3DDevice->CreateVertexBuffer(index * sizeof(CUSTOMVERTEX),
                                              0, D3DFVF_CUSTOMVERTEX,
                                              D3DPOOL_DEFAULT, &pVertexBuffer);

     VOID* pV;
     pVertexBuffer->Lock(0,0, (BYTE**)&pV, 0);
     memcpy(pV, pVertexList, sizeof(CUSTOMVERTEX)*index);
     pVertexBuffer->Unlock();


     g_pD3DDevice->SetStreamSource(0, pVertexBuffer, sizeof(CUSTOMVERTEX));
     g_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
     g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, index/3);//dwNumOfPolygons


     pVertexBuffer->Release();
}

//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
Now to make the sphere cooler....and not to make the whole tutorial to complex, I added a couple of functions to add light to the sphere.

//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
void setlight()
{

//Set a light up?
     D3DLIGHT8 d3dLight;

     //Initialize the light structure.
     ZeroMemory(&d3dLight, sizeof(D3DLIGHT8));

     //Set up a white point light at (0, 0, -10).
     d3dLight.Type = D3DLIGHT_POINT;

     d3dLight.Diffuse.r = 1.0f;
     d3dLight.Diffuse.g = 1.0f;
     d3dLight.Diffuse.b = 1.0f;
     
     d3dLight.Ambient.r = 0.0f;
     d3dLight.Ambient.g = 0.0f;
     d3dLight.Ambient.b = 0.0f;
     
     d3dLight.Specular.r = 0.0f;
     d3dLight.Specular.g     = 0.0f;
     d3dLight.Specular.b     = 0.0f;

     d3dLight.Position.x     = 0.0f;
     d3dLight.Position.y     = 10.0f;//2
     d3dLight.Position.z     = -10.0f;

     d3dLight.Attenuation0 = 1.0f;
     d3dLight.Attenuation1 = 0.0f;
     d3dLight.Attenuation2 = 0.0f;
     d3dLight.Range = 100.0f;


     g_pD3DDevice->SetLight(0, &d3dLight);
     g_pD3DDevice->LightEnable(0,TRUE);
     //Turn on lighting
  g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

     g_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(5,100,32));
}


void setmaterial()
{
     D3DMATERIAL8 matMaterial;
     D3DCOLORVALUE rgbaDiffuse= {1.0, 1.0, 1.0, 0.0};
     D3DCOLORVALUE rgbaAmbient = {1.0, 1.0, 1.0, 0.0};
     D3DCOLORVALUE rgbaSpecular = {0.0, 0.0, 0.0, 0.0};
     D3DCOLORVALUE rgbaEmissive = {0.0, 0.0, 0.0, 0.0};

     matMaterial.Diffuse = rgbaDiffuse;
     matMaterial.Ambient = rgbaAmbient;
     matMaterial.Specular = rgbaSpecular;
     matMaterial.Emissive = rgbaEmissive;
     matMaterial.Power = 100.0f;

     g_pD3DDevice->SetMaterial(&matMaterial);


}
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
The DirectX Initilisation and De-Initilisation functions.
//Main header file for the XDK
#include <xtl.h>

LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));
     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
     d3dpp.BackBufferCount = 1;
     d3dpp.EnableAutoDepthStencil = TRUE;
     d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
   g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);

     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
     g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
     //Turn on z-buffering
     g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
     //Turn off culling - so we can see the back of the sphere :)
     g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
}

void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}

And finally the entry point...the point where it all starts.
//Application entry point
void __cdecl main()
{
     InitialiseD3D();

       setlight();
       setmaterial();
     while(true)
     {
           //Clear the backbuffer to black
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();

                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 //camera
                 D3DXMATRIX view_matrix, matProj;
                 D3DXMatrixLookAtLH(&view_matrix,&D3DXVECTOR3( 0.0f, 0.0f,-5.0f ),
                                  &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                                  &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ));
                 g_pD3DDevice->SetTransform(D3DTS_VIEW,&view_matrix);
                 D3DXMatrixPerspectiveFovLH(&matProj, //Result Matrix
                             D3DX_PI/4,//Field of View, in radians. (PI/4) is typical
                             ((float)600 / (float)400),     //Aspect ratio
                             1.0f,     //Near view plane
                             1000.0f ); // Far view plane
                 g_pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj );
                 //end camera

                 //Okay this all may seem scary for someone new to matrix's but these few lines
                 //of code rotate our sphere so that we can make sure its round :)
                 D3DXMATRIX matWorld;
                 D3DXMATRIX trans_matrix;   //Our translation matrix
                 D3DXMATRIX y_rot_matrix;
                 static float y_rot=0;
                 y_rot-=0.001f;
                 //Set up the rotation matrix for the triangle
                 D3DXMatrixRotationY(&y_rot_matrix,y_rot);
                 //Set up the translation matrix (move it over to the left a bit)
                 D3DXMatrixTranslation(&trans_matrix,-1.0,0.0f,0.0f);
                 //Combine out matrices
                 D3DXMatrixMultiply(&matWorld,&y_rot_matrix,&trans_matrix);
                 //Set our World Matrix
                 g_pD3DDevice->SetTransform(D3DTS_WORLD,&matWorld );

                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 DrawSphereTriangleList();

           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
     }    

     void CleanUp();
}

Now if you go through the code slowly, you'll find the only code you need to know is the DrawSphereTriangleList() function...which of course is a beauty to try and understand in one go.  But most of the other code is for effect - e.g. I've added a few lines in the main() function which rotates our world...so our lovely sphere is rotated slowly.


Prt7 - A Complex Bunny Shape...
by [email protected]
Hehehehe, a bunny shape and more... well to keep things simple and to introduce the concept of how 3D file systems worked, I came across a very simple file system called .plg.  Its a text based file system and you can open it with notepad.  Let me show you a snippet of what lies inside (Oct.plg:
Oct-Shape 6 8

1.0 0.0 0.0
-1.0 0.0 0.0
0.0 1.0 0.0
0.0 -1.0 0.0
0.0 0.0 1.0
0.0 0.0 -1.0

109 3 4 1 3
109 3 4 3 0
109 3 4 0 2
109 3 4 2 1
109 3 5 0 3
109 3 5 3 1
109 3 5 1 2
109 3 5 2 0
We have the shape name followed by the number of vertices and then the number of sides, e.g. 6 vertices and 8 triangles.  Now what follows might be a bit wobbly to a new 3D programmer, but its a list of all the vertices once, for example a cube would have 8 vertices ... one for each corner.
So after that, we have the index of each face, so "109 3 4 1 3", 109 is the colour (0-255), 3 is the number of sides for this shape (3 for a triangle), followed by the index into the vertice list, so we would have 4th joined to the 1st then joined to the 3rd vertices and closing it would make our triangle.
Now believe it or not, this is how nearly all 3D formats work, .md2 (quake format), .3ds (3d studio max format) all use this format... and many many more.
Keeping my code simple, I've put the DirectX initialisation and cleanup functions in a separate file "init.cpp" and the main code in main.cpp.  It makes things simpler to follow, and well its rare that you'll ever need to change init.cpp.
Grab hold of your chair.... in we go....
/***************************************************************************/
/*                                                                         */
/* FileName: init.cpp                                                      */
/*                                                                         */
/* Details: shapes from files.                                             */
/*                                                                         */
/* Author: [email protected]                                           */
/*                                                                         */
/***************************************************************************/

//Main header file for the XDK
#include <xtl.h>

LPDIRECT3D8 g_pD3D = NULL;                      // DirectX Object
LPDIRECT3DDEVICE8 g_pD3DDevice = NULL;          // Screen Object

void InitialiseD3D()
{
   g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
   D3DPRESENT_PARAMETERS d3dpp;
   ZeroMemory(&d3dpp, sizeof(d3dpp));
     d3dpp.BackBufferWidth = 640;
     d3dpp.BackBufferHeight = 480;
     d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
     d3dpp.BackBufferCount = 1;
     d3dpp.EnableAutoDepthStencil = TRUE;
     d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
   g_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
                                  D3DCREATE_HARDWARE_VERTEXPROCESSING,
                                                  &d3dpp, &g_pD3DDevice);

     //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
     //Our 3D shapes are going to use a colour defined by us, and we don't have
     //any lighting so we set the lighting to false.
     g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, false);
     //Turn on z-buffering
     g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
     
     g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
}

void CleanUp()
{
   g_pD3DDevice->Release();
   g_pD3D->Release();
}
Well if you look at the above code is should be boring and repetaive...so don't worry about it...all your exciting and wonderful directX code has it in :)
Now for the sticky slimmy code that makes you wanna queez...
/***************************************************************************/
/*                                                                         */
/* FileName: main.cpp                                                      */
/*                                                                         */
/* Details: shapes from files.                                             */
/*                                                                         */
/* Author: [email protected]                                           */
/*                                                                         */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/

char shapefilename[] = "D:\Rabbit.plg";

// Main header file for the XDK
#include <xtl.h>

// Standard librarys used to read in our data from file. 3D object.
#include <stdio.h>
#include <fstream.h>

#include "init.cpp"

// Here are a couple of structures to store our data, its so much tidier to use
// structures, as we can store an XYZ value in the stXYZ stucture, an if we need
// to access it we can just go stXYZ myP;  myP.x = 0;  etc.
struct stXYZ
{
     float x, y, z;
};

struct stFace
{
     int p1;
     int p2;
     int p3;
};
// These are our global variables, I'm going to load the 3D object in, and put
// them in here.

// Global variables.  These will hold the data that contains our shapes.
stXYZ*      pVerts;
stFace*           pFaces;
int iNum_vectors  = 0;
int iNum_polys          = 0;

// Our wonderful wonderful loader function, if you look at this funciton you'll
// notice that it works on either the xdk and windows, and would even work
// on linux as it uses the standard librarys.
int loadshape(char *filename)
{
     long        int    colour;
   int         i;
   char        temp[16];

   // Error if iNum_vectors = 0 when returning from function
     int iNum_points         = 0; // Temp used to determine if the side is made up
                              // of 3 sides (triangle or more).
     
   ifstream fin(filename);

   if(!fin) {
       // If where in here, and error occured opening the file.
       return 0;
   }

   // read in number of points and polygons in object
   fin >> temp >> iNum_vectors >> iNum_polys;
   // Initialize our arrays
   pVerts        = new stXYZ[iNum_vectors];

   pFaces        = new stFace[iNum_polys];


   // read in all the vectors used in this object
   for (i=0; i<iNum_vectors; i++)
   {
       fin >> pVerts.x >> pVerts.y >> pVerts.z;
   }

   // now read in all of the polygon data
   for (i=0; i<iNum_polys; i++)
   {
       fin >> colour;
       // We could use the colour in some way.. but I'm just going to set
           // all the faces to the same colour... keep it nice and simple :)

       fin >> iNum_points;

           if( iNum_points > 3 )
           {
                 return 0; // Our code only supports triangles.
           }

       fin >> pFaces.p1 >> pFaces.p2 >> pFaces.p3;
   
     }
   fin.close();
}

// Now upto now there's been "No" directX used, only knowledge of the 3d file,
// and the c and c++ standard librarys.  So what I'm going to do, is load the
// vertex points into the directX buffers and display it.  This isn't very
// efficent... but its simple to follow :)
void DrawShape()
{
     
     struct CUSTOMVERTEX
     {
           FLOAT x, y, z;
           DWORD colour;
     };

     CUSTOMVERTEX *myVertices = new CUSTOMVERTEX[iNum_polys*3];
     // Total number of triangles is iNum_polys
     int index=0;
     for (int i=0; i<iNum_polys; i++)
     {
           myVertices[index].x = pVerts[pFaces.p1].x;
           myVertices[index].y = pVerts[pFaces.p1].y;
           myVertices[index].z = pVerts[pFaces.p1].z;
           myVertices[index].colour = 0xff00ffff;

           index++;
           myVertices[index].x = pVerts[pFaces.p2].x;
           myVertices[index].y = pVerts[pFaces.p2].y;
           myVertices[index].z = pVerts[pFaces.p2].z;
           myVertices[index].colour = 0xff0ff00f;

           index++;
           myVertices[index].x = pVerts[pFaces.p3].x;
           myVertices[index].y = pVerts[pFaces.p3].y;
           myVertices[index].z = pVerts[pFaces.p3].z;
           myVertices[index].colour = 0xff00ffff;

           index++;
     }
     
     
     LPDIRECT3DVERTEXBUFFER8 pVertexBuffer = NULL;
     UINT D3DFVF_CUSTOMVERTEX =  (D3DFVF_XYZ|D3DFVF_DIFFUSE);
     
     g_pD3DDevice->CreateVertexBuffer(3 * iNum_polys * sizeof(CUSTOMVERTEX),
                                              0, D3DFVF_CUSTOMVERTEX,
                                              D3DPOOL_DEFAULT, &pVertexBuffer);
     VOID* pV;
     pVertexBuffer->Lock(0,0, (BYTE**)&pV, 0);
     memcpy(pV, myVertices, sizeof(CUSTOMVERTEX)*iNum_polys*3);
     pVertexBuffer->Unlock();

     g_pD3DDevice->SetStreamSource(0, pVertexBuffer, sizeof(CUSTOMVERTEX));
     g_pD3DDevice->SetVertexShader(D3DFVF_CUSTOMVERTEX);
     g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, iNum_polys);


     pVertexBuffer->Release();
     delete myVertices;
     
}


void Render()
{
                 //Clear the backbuffer to black
           g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0);
           //Begin the scene
           g_pD3DDevice->BeginScene();


                 //camera camera camera camera camera camera camera camera camera camera
                 D3DXMATRIX view_matrix, matProj;
                 D3DXMatrixLookAtLH(&view_matrix,&D3DXVECTOR3( 0.0f, 0.0f,-4.0f ),
                                  &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                                  &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ));
                 g_pD3DDevice->SetTransform(D3DTS_VIEW,&view_matrix);
                 D3DXMatrixPerspectiveFovLH(&matProj, //Result Matrix
                             D3DX_PI/4,//Field of View, in radians. (PI/4) is typical
                             ((float)600 / (float)400),     //Aspect ratio
                             1.0f,     //Near view plane
                             1000.0f ); // Far view plane
                 g_pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj );
                 //end camera end camera end camera end camera end camera end camera end

                 //Okay this all may seem scary for someone new to matrix's but these few lines
                 //of code rotate our spher
Logged

predatorkill

  • Archived User
  • Newbie
  • *
  • Posts: 5
The Ultimate Xdk Tutorial
« Reply #1 on: March 10, 2003, 03:00:00 AM »

//Okay this all may seem scary for someone new to matrix's but these few lines
                 //of code rotate our sphere so that we can make sure its round :)
                 D3DXMATRIX matWorld;
                 D3DXMATRIX trans_matrix;   //Our translation matrix
                 D3DXMATRIX y_rot_matrix;
                 static float y_rot=0;
                 y_rot-=0.005f;
                 //Set up the rotation matrix for the triangle
                 D3DXMatrixRotationY(&y_rot_matrix,y_rot);
                 //Set up the translation matrix (move it over to the left a bit)
                 D3DXMatrixTranslation(&trans_matrix,-1.0,0.0f,0.0f);
                 //Combine out matrices
                 D3DXMatrixMultiply(&matWorld,&y_rot_matrix,&trans_matrix);
                 //Set our World Matrix
                 g_pD3DDevice->SetTransform(D3DTS_WORLD,&matWorld );

                 //NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
                 DrawShape();

           //End the scene
           g_pD3DDevice->EndScene();

           //Filp the back and front buffers so that whatever has been rendered on the back buffer
           //will now be visible on screen (front buffer).

           g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}

/////////////////////////////////////////////////////////////////////////////////////////
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW
//NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW

//Application entry point
void __cdecl main()
{
       InitialiseD3D();
       
       loadshape(shapefilename);

     while(true)
     {
        Render();  
     }    

     void CleanUp();
}
There's a lot of code there, I hope some of it is easy to follow the main pieces of code you should be chewing up slowly is "loadshape(..)" and the "DrawShape()" functions.  We call "loadshape(..)" once at the start which loads our data in from the file, then in our main loop we call "DrawShape()" over and over again and it takes our vertices and face data that we loaded in and puts it into a list of triangle faces and puts it to the screen.
Looking at the first part of this code you might realise that its standard C... no DirectX or anthing special...just C...hmmm...I know what your thinking... well you can use other C functions and load in other file formats such as .3ds for .md3 etc...yup.. .its as easy as that.




3DS Part 1- "the bits inside" - The 3DS format from start to finish!
This tutorial is going to be broken down into two sections the first will explain and show the workings of the .3ds file format as used in 3D Studio Max etc, and is a valuable piece of knowledge. Why?  Well nearly all 3d packages will allow the import/export of it so. The second part will put it to good use and show it in action, as we develop a working class that we can re-use over and over again.
So before I start showing you code, and talking about chunks I better give a quick explanation of how it all works inside. First thing first, if your going to work with the .3ds format, you better get used to chunksÂ… yup you heard right, chunks. As for every piece of information in the .3ds file there is a chunk which is 6 bytes big, which tells us the size of the chunk and what data is in it.
Our chunk:
2 bytes = Chunk ID.  (because its 2 bytes, a short int can be used to represent it).
4 bytes = Chunk length in bytes. (again an unsigned integer is 4 bytes, perfect to represent the size of our chunk).
Put the computer downÂ…donÂ’t smash the screen because IÂ’ve lost you at such an early stageÂ…lol. Lets do this with code so that you can understand what I mean.
Okay okay, this is as simple and easy to follow as I can make it. All it does it make sure that we are dealing with a .3ds file. How does it work? Well if you donÂ’t know what fread(..) function is your in trouble. ItÂ’s a standard c function which is included in the <stdio.h> library.
#include <stdio.h>
// I'm going to read in the data from the file and output it
// to a text file called fileinfo.txt, which will be in the
// same directory as the .3ds file and .exe file.
void output(char *string)
{
 FILE* fp = fopen("fileinfo.txt", "a+");
 fprintf(fp,"%s", string);
 fclose(fp);
}

// Just pick any .3ds file at random, and specify its name.
char filename[] = "box.3ds";
 
// Program entry point.
void main()
{
 // Part-1- Open the file
 FILE* fp = fopen(filename, "rb");

 // Part-2- Create some variables to read our data in.
 char ID[2];
 unsigned int chunksize;

 // Part-3- Read in the 6 bytes of data from the start of the file.
 //     Buffer to put our data in
 //     |  Size in byte of data to read
 //     |  |  Number of times to read (e.g. 2 sets of one byte.
 //     |  |  |  Pointer to the open file.
 //     |  |  |  |
 fread(ID, 2, 1, fp);
 fread(&chunksize, 4, 1, fp);

 // Part-4- Close our file and display what we have read in.
 fclose(fp);

 // Large text buffer.
 char buff[200];
 sprintf(buff, "First byte,  ID[0]: %xn", ID[0]);
 output(buff);

 sprintf(buff, "Second byte, ID[1]: %xn", ID[1]);
 output(buff);

 sprintf(buff, "Next 4 bytes, chunksize: %un", chunksize);
 output(buff);
}
So whats happening in this section of code, and what does it tell us?  Well if you run it youÂ’ll get an output file called “fileinfo.txt” which contains:
First byte, ID[0]: 4d
Second byte, ID[1]: 4d
Next 4 bytes, chunksize: 595
About the code, we open the .3ds file, (in this case box.3ds) and we read in the very first byte, and it is 4D in hex, the byte right after it is read in and has the value of 4D.  This is the chunk ID (4D4D).  We read the next 4 bytes into an unsigned integer, and what does it have??  Well itÂ’s the size of the chunkÂ… and the very first chunk (ID of 4D4D) contains all the other chunks and so the chunksize is the size of the file.
So if you open the file properties and check its file size you will find out that the size of the file is 595 bytes J.

Now where has this taken us?  You should know what a chunk is, its best described as:
struct stChunk
{
 unsigned short ID;  // 2 bytes
 unsigned int size;  // 4 bytes
};
So now our code would become:
#include <stdio.h>
void output(char *string)
{
 FILE* fp = fopen("fileinfo.txt", "a+");
 fprintf(fp,"%s", string);
 fclose(fp);
}

// Just pick any .3ds file at random, and specify its name.
char filename[] = "box.3ds";
void main()
{
 // Part-1- Open the file
 FILE* fp = fopen(filename, "rb");

 // Part-2- Create some variables to read our data in.
 struct stChunk
 {
   unsigned short ID;  // 2 bytes
   unsigned int size;  // 4 bytes
 };

 stChunk myChunk;

 fread(&myChunk.ID, 2, 1, fp);   // Read in 2 bytes.
 fread(&myChunk.size, 4, 1, fp); // Read in 4 bytes.
 fclose(fp);


 // Large text buffer.
 char buff[200];
 // Write our data to the text file.
 sprintf(buff, "Chunk ID 0x%xn", myChunk.ID);
 output(buff);

 sprintf(buff, "Size of Chunk: %un", myChunk.size);
 output(buff);
}
Well its all becoming nice and tidy now... we just read in the chunks and then we know what data is in the chunk from its ID, and how big it is from the size of the chunk.  The next big thing is to know what all the chunks are and what they do.  Now there's a big big load of ID's which does everything from camera position to the simple x,y,z position of the vertex.  I'll be putting together a whole document describing them later on when I get time, but I'm sure with a bit of help from google and yahoo you can find some if you really need to :)
So the plot thickens...
//>----- Entry point (Primary Chunk at the start of the file ----------------
#define           PRIMARY                 0x4D4D

//>----- Main Chunks --------------------------------------------------------
#define           EDIT3DS                 0x3D3D  // Start of our actual objects
#define           KEYF3DS                 0xB000  // Start of the keyframe information

//>----- General Chunks -----------------------------------------------------
#define           VERSION                 0x0002
#define           MESH_VERSION      0x3D3E
#define           KFVERSION         0x0005
#define           COLOR_F                 0x0010
#define           COLOR_24          0x0011
#define           LIN_COLOR_24      0x0012
#define           LIN_COLOR_F       0x0013
#define           INT_PERCENTAGE    0x0030
#define           FLOAT_PERC        0x0031
#define           MASTER_SCALE      0x0100
#define           IMAGE_FILE        0x1100
#define           AMBIENT_LIGHT     0X2100

//>----- Object Chunks -----------------------------------------------------
#define           NAMED_OBJECT      0x4000
#define           OBJ_MESH          0x4100
#define           MESH_VERTICES     0x4110
#define           VERTEX_FLAGS      0x4111
#define           MESH_FACES        0x4120
#define           MESH_MATER        0x4130
#define           MESH_TEX_VERT     0x4140
#define           MESH_XFMATRIX     0x4160
#define           MESH_COLOR_IND    0x4165
#define           MESH_TEX_INFO     0x4170
#define           HEIRARCHY         0x4F00


//>----- Material Chunks ---------------------------------------------------
#define           MATERIAL          0xAFFF
#define           MAT_NAME          0xA000
#define           MAT_AMBIENT       0xA010
#define           MAT_DIFFUSE       0xA020
#define           MAT_SPECULAR      0xA030
#define           MAT_SHININESS     0xA040
#define           MAT_FALLOFF       0xA052
#define           MAT_EMISSIVE      0xA080
#define           MAT_SHADER        0xA100
#define           MAT_TEXMAP        0xA200
#define           MAT_TEXFLNM       0xA300

#define           OBJ_LIGHT         0x4600
#define           OBJ_CAMERA        0x4700

//>----- KeyFrames Chunks --------------------------------------------------
#define           ANIM_HEADER       0xB00A
#define           ANIM_OBJ          0xB002

#define           ANIM_NAME         0xB010
#define           ANIM_POS          0xB020
#define           ANIM_ROT          0xB021
#define           ANIM_SCALE        0xB022
Now before you give up and think that its a lot to swallow don't worry about it......  These are all the chunk ID's you'll ever need in the real world... what they all do I'll show you in time.  Now me, I've just put them all in a separate file called chunkdetails.cpp as you usually never need to actually remember all there ID's... its better to go, " if( ID = = PRIMARY) " than if you go, if( ID = = 4D4D).
Believe it or not you've come a long way in such a short time.  If you've grasped this chunk stuff, your home and dry practically...  just stay with me a few more lines and we'll get you out ... you better go and get some coffee as well, make it a 400g jar.
"News News".... skipping chunks isn't illegal.
Okay this is very important, especially if your new to the .3ds format.  If you don't know a chunk ID or you just don't want the data from that chunk, just read past it... you know how big it is, so you can just read to the end of the chunk and read in the next chunk ID and size.  Cool ?  :)
So where next?...  well people new to the format could get lost very very easily at this point, so lets just get the vertex data...you know...the points that make up our shape... x,y,z for each triangle... still with me?  If at this point your wondering what a vertex is... I'm wondering how you got this for...lol
Okay the code that follows is the foundation of creating your own .3ds reader... from this you should be able to go a long way, it allows you to read in the chunk and read past it.  Once I show you how the data is arranged in the chunks, e.g. how to get the vertex's the colours etc...your fixed for life :)
// Our .3DS file that we'll use for testing.
#define FILENAME "box.3ds"

/***************************************************************************/

// These few lines are compiler directives that include the windows librarys
// during linking.  You could instead inside visual studio goto Project->
// settings" menu and under the "Link" tab add the necessary librarys instead.
#include <stdio.h>


// I've put the chunk defenitions in a seperate file on there own, called
// chunkdetails.h, which we'll include here.
#include "chunkdetails.h"

/***************************************************************************/
/*                                                                         */
/* Write feedback information to a file - so we can understand what is     */
/* really happening inside that .3ds file.                                 */
/*                                                                         */
/***************************************************************************/

void output(char *str)
{
     FILE *fp;
     fp = fopen("filedata.txt", "a+");
     fprintf(fp, "%s", str);
     fclose(fp);
}

/***************************************************************************/
/*                                                                         */
/* Some user functions to make the reading of the .3ds file easier         */
/*                                                                         */
/***************************************************************************/

struct stChunk
{
     unsigned short ID;
     unsigned int length;
     unsigned int bytesRead;
};

void ReadChunk(FILE*fp, stChunk *pChunk)
{
     unsigned short ID             = 0;
     unsigned int bytesRead        = 0;
     unsigned int bChunkLength     = 0;

     bytesRead = fread(&ID, 1, 2, fp);

     bytesRead += fread(&bChunkLength, 1, 4, fp);

     pChunk->ID          = ID;
     pChunk->length      = bChunkLength;
     pChunk->bytesRead = bytesRead;

}

void SkipChunk(FILE*fp, stChunk *pChunk)
{
     int buffer[50000] = {0};

     fread(buffer, 1, pChunk->length - pChunk->bytesRead, fp);
}

/***************************************************************************/
/*                                                                         */
/* Read in .3ds file.                                                      */
/*                                                                         */
/***************************************************************************/

void read3ds()
{
     FILE* pFile;
     pFile = fopen(FILENAME, "rb");
     // Large buffer for our text.
     char buf[1000] = {0};
     
     // I've created a structure called stChunk, which should be self explanatory
     // and holds the ChunkID, its size (in bytes) and a bytes read counter...so
     // as we read into it we know how many bytes are left.
     stChunk Chunk = {0};
     // This is where it starts to become organised, now instead of using fread etc
     // and specifying to read in the chunk information, we just create a funciton
     // called ReadChunk and it will read the information into our chunk.

     // This is our very first chunk read... and its details should be:
     // First Chunk ID: 0x 4d 4d
   // Size of Chunk: 595
     ReadChunk(pFile, &Chunk);
     
     sprintf(buf, "Chunk ID: 0x %x n", Chunk.ID);
     output(buf);

     // Now once we are sure we have a .3ds file (e.g. the very very first chunk
     // we read in has an ID of 4D4D then we can read in all the sub chunks.
     while(Chunk.bytesRead < Chunk.length)
     {
           stChunk tempChunk = {0};
           ReadChunk(pFile, &tempChunk);

           sprintf(buf, "tChunk ID: 0x %04x n", tempChunk.ID);
           output(buf);
           
           // This function is simple it will just read past all the contents of a
           // chunk.
           SkipChunk(pFile, &tempChunk);

           Chunk.bytesRead += tempChunk.length;
     }

     fclose(pFile);
}

/***************************************************************************/
/*                                                                         */
/* The program entry point, this is where we start and end.                */
/*                                                                         */
/***************************************************************************/

void main()
{
     // I'm going to put the 3ds information in seperate files, so that as we
     // go along we can start to create a independent set of function (and
     // eventually a class) which will work with windows, and other platforms.
     read3ds();
}
So what does the output file look like?  filedata.txt output:
Chunk ID: 0x 4d4d
       Chunk ID: 0x 0002
       Chunk ID: 0x 3d3d
       Chunk ID: 0x b000
It may not look like much, but if you look at the meaning for each 3DS chunk you'll soon start to get it.
Chunk ID: 0x4d4d...well hope by now you know this is the entry point for our .3ds file!  Its the very first first chunk, and its size is the size of the file in bytes. Next is 0x002 which if you look at the file chunkdetails.cpp you'll find that it is called "VERSION", now I hope this speaks for itself, but the data in this chunk is an integer which represents the version number e.g. 3.   Next is 0x3d3d which if you look at the chunkdetails.cpp file again you'll see that is called "EDIT3DS", now all your 3D shape information goes in here... colour, number of vertices etc.  And finalllllyyyy we have a door number 3, 0xb000 which is the animation details ("KEYF3DS"), as you can animate an object in 3d studio max and save its key frame animation data in here.
Remember this is a beginners guide, so the only information I'm going to get for you is the vertex data!  With this vertex data you can draw the shape, later on I'll show you how to get the other information such as colour, key frame details etc.
Now the following piece of code is the reason why I add "download code" options to my web site.  Its a big piece of code, but if you've been following me up to now you'll find it pretty simple and easy to follow.  This is the first time I've actually included "chunkdetails.h"  in the file.
// Our .3DS file that we'll use for testing.
#define FILENAME "box.3ds"

/***************************************************************************/

// These few lines are compiler directives that include the windows librarys
// during linking.  You could instead inside visual studio goto Project->
// settings" menu and under the "Link" tab add the necessary librarys instead.
#include <stdio.h>

// I've put the chunk definitions in a separate file on there own, called
// chunkdetails.h, which we'll include here.
#include "chunkdetails.h"

/***************************************************************************/
/*                                                                         */
/* Write feedback information to a file - so we can understand what is     */
/* really happening inside that .3ds file.                                 */
/*                                                                         */
/***************************************************************************/

void output(char *str)
{
     FILE *fp;
     fp = fopen("filedata.txt", "a+");
     fprintf(fp, "%s", str);
     fclose(fp);
}

/***************************************************************************/
/*                                                                         */
/* Some user functions to make the reading of the .3ds file easier         */
/*                                                                         */
/***************************************************************************/

struct stChunk
{
     unsigned short ID;
     unsigned int length;
     unsigned int bytesRead;
};

void ReadChunk(FILE*fp, stChunk *pChunk)
{
     unsigned short ID             = 0;
     unsigned int bytesRead        = 0;
     unsigned int bChunkLength     = 0;

     bytesRead = fread(&ID, 1, 2, fp);

     bytesRead += fread(&bChunkLength, 1, 4, fp);

     pChunk->ID          = ID;
     pChunk->length      = bChunkLength;
     pChunk->bytesRead = bytesRead;
}

void SkipChunk(FILE*fp, stChunk *pChunk)
{
     int buffer[50000] = {0};

     fread(buffer, 1, pChunk->length - pChunk->bytesRead, fp);
}
/***************************************************************************/
/*                                                                         */
/* This is what I like to call a chunk feedback function...lol...          */
/* I thought it was tidier to just pass the chunk to the function every    */
/* time I read in a new chunk and have it display the chunk details, e.g.  */
/* its ID and its size (in bytes).                                         */
/*                                                                         */
/***************************************************************************/

void DisplayChunkInfo(stChunk* pChunk)
{
     char buf[1000] = {0};
     sprintf(buf, "Chunk ID: 0x %04x   Size of Chunk: %un", pChunk->ID, pChunk->length);
     output(buf);
}
/***************************************************************************/
/*                                                                         */
/* Now we are going to display all the branches of the "EDIT3DS" chunk.    */
/* E.g. chunks within chunks :)                                            */
/*                                                                         */
/***************************************************************************/

void ReadEDIT3DS(FILE *fp, stChunk* Chunk)
{
     // Just as for the main chunk, I've passed the chunk detais to the function,
     // so I know how big it is, and so I can just keep reading all the chunks in
     // it until I reach the end, then return.
     while(Chunk->bytesRead < Chunk->length)
     {
           stChunk tempChunk = {0};
           ReadChunk(fp, &tempChunk);
           DisplayChunkInfo(&tempChunk);
           
           // I'll add a case statement later, but first things first lets take
           // a looksy inside the "EDIT3DS" (0x3D3D) chunk, and see what sub chunks
           // are really in there.
           SkipChunk(fp, &tempChunk);
           
           
           Chunk->bytesRead += tempChunk.length;
     }
}
/***************************************************************************/
/*                                                                         */
/* Read in .3ds file.                                                      */
/*                                                                         */
/***************************************************************************/

void read3ds()
{
     FILE* pFile;
     pFile = fopen(FILENAME, "rb");
     
     stChunk Chunk = {0};
     ReadChunk(pFile, &Chunk);
     
     DisplayChunkInfo(&Chunk);

     // Loop and loop around, until we reach the end of the big chunk, the first
     // chunk the chunk with the ID of 4d4d :)
     while(Chunk.bytesRead < Chunk.length)
     {
           stChunk tempChunk = {0};
           ReadChunk(pFile, &tempChunk);
           DisplayChunkInfo(&tempChunk);

           unsigned int bytesRead = 0;
           
           switch( tempChunk.ID)
           {
           // Now where going to go to another function to read in all our 3D
           // shape information....why hang around in here, when it will only
           // make single complex function... best to break it up into tidy sections.
           case EDIT3DS:
                 ReadEDIT3DS(pFile, &tempChunk);
                 bytesRead = tempChunk.length;
                 break;
           // Where not going to bother with the animation chunks at this early
         // stage, so we just read past them.
           case KEYF3DS:
                 SkipChunk(pFile, &tempChunk);
                 bytesRead = tempChunk.length;
                 break;
           // Other junks, like junks added to newer versions, and the version
           // chunk I'm just going to skip, as its a tutorial and it just makes
           // the code hard to follow.
           default:
                 SkipChunk(pFile, &tempChunk);
                 bytesRead = tempChunk.length;
           }    
           Chunk.bytesRead += bytesRead;
     }
     fclose(pFile);
}

/***************************************************************************/
/*                                                                         */
/* The program entry point, this is where we start and end.                */
/*                                                                         */
/***************************************************************************/
void main()
{
     read3ds();
}
So the output file is starting to grow now, and it contains a lot of juicy info that you should be able to look at and see what's going on inside all this..    filedata.txt contains:
Chunk ID: 0x 4d4d Size of Chunk: 595
       Chunk ID: 0x 0002 Size of Chunk: 10
       Chunk ID: 0x 3d3d Size of Chunk: 360
               Chunk ID: 0x 3d3e Size of Chunk: 10
               Chunk ID: 0x 0100 Size of Chunk: 10
               Chunk ID: 0x 4000 Size of Chunk: 334
       Chunk ID: 0x b000 Size of Chunk: 219
Let me explain what this information tells us, the more times I tell you the better... I don't want you forgetting or getting lost along the way.
Chunk ID: 0x 4d4d Size of Chunk: 595    -> "PRIMARY" chunk entry point...and the size of the file...595 bytes.
Chunk ID: 0x 0002 Size of Chunk: 10      -> "VERSION" chunk (sub chunk of Primary Chunk), and is 10 bytes, which includes the 6 bytes for the  header, the other 4 bytes is an unsigned integer which you could read in and it would represent the version number.
Chunk ID: 0x 3d3d Size of Chunk: 360    -> "EDIT3DS" chunk, which I like to think of as the 3d data chunk, as its our 3d objets for our shape which is in here.  And the same as before, its a baby of the primary chunk.
Chunk ID: 0x 3d3e Size of Chunk: 10      -> "MESH_VERSION" chunk, simply the version used to store the vertex data....again 10 bytes, 6 for the chunk, and 4 for the unsigned integer which should be a number.... we can ignore this.  Later on we'll add some error checking to make sure its a 3, for version 3 for the .3ds format.
Chunk ID: 0x 0100 Size of Chunk: 10     -> "MASTER_SCALE" chunk - this is a scale value, telling us?  Yup how much to scale our object, 2 for twice as big 4 for four times as big.... I think you get the picture.
Chunk ID: 0x 4000 Size of Chunk: 334    -> "NAMED_OBJECT" chunk - inside here we have our object, in this case our cube :)  We could have numerous NAMED_OBJECT chunks, one for a sphere, another cube etc...which we could read in.... but we only going to read in one object.
Chunk ID: 0x b000 Size of Chunk: 219   -> "KEYF3DS" chunk - yup the animation stuff... as I said... we don't want to open pandoras box of code yet.
At his point in time, I hope your coming to terms with bytes and what I've been doing!  Remember, char is a byte, a integer is 4 bytes, short integer is 2 bytes, and a float is 4 bytes.

Logged

predatorkill

  • Archived User
  • Newbie
  • *
  • Posts: 5
The Ultimate Xdk Tutorial
« Reply #2 on: March 10, 2003, 03:02:00 AM »

if someone need the document(better word wrap,format) send me a PM so i can email it to you....

hope this topic help lots of people....
Logged

caustic

  • Archived User
  • Newbie
  • *
  • Posts: 25
The Ultimate Xdk Tutorial
« Reply #3 on: March 10, 2003, 03:18:00 AM »

Logged

predatorkill

  • Archived User
  • Newbie
  • *
  • Posts: 5
The Ultimate Xdk Tutorial
« Reply #4 on: March 10, 2003, 03:23:00 AM »

this site is no longer updated...

check www.xbdev.net
(it's somekind a mirror of the site above(caustic) )
Logged

Cheerio

  • Archived User
  • Hero Member
  • *
  • Posts: 542
The Ultimate Xdk Tutorial
« Reply #5 on: March 10, 2003, 06:46:00 AM »

those sites both dont work
Logged

BenJeremy

  • Archived User
  • Hero Member
  • *
  • Posts: 5645
The Ultimate Xdk Tutorial
« Reply #6 on: March 10, 2003, 06:54:00 AM »

QUOTE (Cheerio @ Mar 10 2003, 10:46 AM)
those sites both dont work

Works for me.... first time from this computer, so it's not cached.
Logged

Cheerio

  • Archived User
  • Hero Member
  • *
  • Posts: 542
The Ultimate Xdk Tutorial
« Reply #7 on: March 10, 2003, 07:04:00 AM »

:unsure: it works now. wierd  
Logged

Wishi

  • Archived User
  • Full Member
  • *
  • Posts: 191
The Ultimate Xdk Tutorial
« Reply #8 on: March 10, 2003, 11:54:00 AM »

Just giving the link to xbdev.net in the initial post would have been a tad easier than three miles of scrolling.
Logged

predatorkill

  • Archived User
  • Newbie
  • *
  • Posts: 5
The Ultimate Xdk Tutorial
« Reply #9 on: March 10, 2003, 01:04:00 PM »

my target was one:
the user have all these lines of code just with a fast drag and copy to his notepad...

not clicking in the link on the site(there are 10 links),waiting for every part to download...

understand?
Logged

razorrifh

  • Archived User
  • Sr. Member
  • *
  • Posts: 329
The Ultimate Xdk Tutorial
« Reply #10 on: March 10, 2003, 02:43:00 PM »

i agree, a link would have been better. also getting the owner of this site to host a mirror of that page would be good too, cuz this site seems to have the slashdot effect (like on allxboxskins.com, or did that go down for some other reason?)

*edit* but thanks for the link  smile.gif
Logged

Mage

  • Archived User
  • Sr. Member
  • *
  • Posts: 482
The Ultimate Xdk Tutorial
« Reply #11 on: March 10, 2003, 04:38:00 PM »

One question, why are you doing int __cdecl main()
cdecl is the default c calling convention, so it's already cdecl.
Logged

Wishi

  • Archived User
  • Full Member
  • *
  • Posts: 191
The Ultimate Xdk Tutorial
« Reply #12 on: March 12, 2003, 10:35:00 AM »

If people are too lazy to click on 10 links then what's the world coming to...

Plus the formatting is rubbish on the topic and makes it miles harder to read.
Logged

razorrifh

  • Archived User
  • Sr. Member
  • *
  • Posts: 329
The Ultimate Xdk Tutorial
« Reply #13 on: March 12, 2003, 06:44:00 PM »

QUOTE (Mage @ Mar 11 2003, 12:38 AM)
One question, why are you doing int __cdecl main()
cdecl is the default c calling convention, so it's already cdecl.

ive got no idea what hes talking about but it seems interesting, any info on this?
Logged

Wishi

  • Archived User
  • Full Member
  • *
  • Posts: 191
The Ultimate Xdk Tutorial
« Reply #14 on: March 13, 2003, 08:11:00 AM »

Well, there's no need not to put it in. It's just being explicit I guess.

Plus since it's for beginners it's best to show everything instead of just excluding it so they at least know what it is if they see it around.

blink.gif
Logged
Pages: [1] 2