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
![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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
![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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
![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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
![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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
![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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
![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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 ![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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 ![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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 ![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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 ![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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 ![Smiley :)](https://forums.xboxscene.org/Smileys/xs/smiley.gif)
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