Skip navigation

This is a very long, very long, very long tutorial about how to get started with D3D in C++.

Check it out!

//////////////////////////////////////////
//                                      //
// Direct3D basics                      //
//                                      //
// You found this at bobobobo's weblog, //
// http://bobobobo.wordpress.com        //
//                                      //
// Creation date:  July 1/09 (HAPPY CANADA DAY! ) //
// Last modified:  July 3/09            //
//                                      //
//////////////////////////////////////////

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <d3d9.h>      // core direct3d
#include <d3dx9.h>     // aux libs

#include <dxerr9.h>    // detailed error messages

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")  // aux libs
#ifdef _DEBUG
#pragma comment(lib,"d3dx9d.lib")
#else
#pragma comment(lib,"d3dx9.lib")
#endif

#pragma comment(lib, "dxerr9.lib")


// Macros.
#define SAFE_RELEASE(ptr) if(ptr) { ptr->Release(); ptr = NULL; }
#define CAST_AS_DWORD(x) *((DWORD*)&x)
#define PI 3.14159

struct Globals
{
  // Wrapping the Win32-related variables up
  // together in their own struct so they don't
  // get in the way of the Direct3D ones
  struct _Win
  {
    HINSTANCE hInstance;    // window app instance
    HWND hwnd;              // handle for the window
    HWND hConsole ;         // handle for the console window
    
    int width, height;      // the desired width and
    // height of the CLIENT AREA
    // (DRAWABLE REGION in Window)
  } win ;

  #pragma region DIRECT3D9 stuff
  ////////////////////////////
  // Declare The IDirect3D9 INTERFACE!!
  // IDirect3D9 interface
  IDirect3D9 * d3d ;        // represents the BEAST
  // that is Direct3D9 itself.  What's it for?
  
  // MSDN SAYS about the IDirect3D9 interface:
  // "Applications use the methods of the IDirect3D9 interface
  // to create Microsoft Direct3D objects and set up the
  // environment. This interface includes methods for
  // enumerating and retrieving capabilities of the device."

  // IDirect3DDevice9
  IDirect3DDevice9 * gpu ;  // represents the GPU

  // MSDN SAYS about the IDirect3DDevice9:  "Applications use the
  // methods of the IDirect3DDevice9 interface to perform
  // DrawPrimitive-based rendering, create resources, work
  // with system-level variables, adjust gamma ramp levels,
  // work with palettes, and create shaders."
  ////////////////////////////

  // The ABOVE TWO variables are
  // very important to this application.
  
  // For both of these, notice how they
  // are BOTH POINTERS to an INTERFACE.

  // What's an interface?  Well, in a few words,
  // an INTERFACE is something you "face" to
  // interact with something.  Thru means of
  // the "INTERFACE" you get information from
  // the underlying system, or send commands
  // to the underlying system, without really
  // having to understand the underlying system
  // at all to do it.  You just have to know what
  // types of commands it expects to get.

  // For example, your car.

  // The "interface" of your car is its steering wheel,
  // its dials on the dash telling you what speed
  // you're going and the RPM's you're at so you
  // don't blow the engine redlining, and also,
  // the gas and brakes, so you can send commands
  // to the car to stop and go.

  // Notice how you don't have to know a THING
  // about how an internal combustion engine works
  // to get the car to go.  Because the car's INTERFACE
  // is SO abstract (simply PUSH THE PEDAL TO GO),
  // working the car becomes incredibly simple.

  // If the car didn't have such an abstract
  // interface (like, if REALLY crummy engineers
  // made a car), then to drive that crummy car,
  // you might have to put your hands into
  // the engine and carefully push
  // vaporized gas underneath a piston, then
  // push down on the piston until it goes POP!
  // Then the car would be going!
  
  // Anyway, point is, working with a system
  // THROUGH ITS INTERFACE that the system defines
  // makes working with the system SO easy, and
  // the system itself is a black box -- its internal
  // workings are hidden from you.  Like, you
  // don't even have to know what an internal
  // combustion engine IS to be able to
  // work a modern car.  Heck, for all you know,
  // it might not even be an internal combustion
  // engine!  (It might be electric).  That's another
  // beauty about interfaces:  You can swap out
  // the nitty gritty details of the implementation
  // (e.g. software updates / patches, or the
  // difference between Direct3D9 June 2008 release
  // and March 2009 release) without affecting 
  // the programs that USE those interfaces, so long
  // as you have not CHANGED the interface itself.

  // IN the case of Direct3D9, an IDirect3DDevice9 *
  // is a pointer to, let's just say the "dashboard" ON TOP
  // OF the Direct3D9 RENDERING MACHINE.

  // Inside, the Direct3D 3D graphics "engine" is VERY complex!
  // Especially when you get to rendering textures in 3D..
  // (see this book if you want to learn that stuff!)
  
  // But with Direct3D9, you don't have to understand
  // HOW 3d graphics actually gets drawn.  You only have to
  // understand the format that D3D expects, and pass it
  // your data that you want drawn in that format.

  // OK?  So hopefully that made a little bit of sense.
  // To me Direct3D seems slightly more "centralized"
  // than OpenGL does.  With OpenGL, the "interface"
  // is a set of C-style functions that all begin
  // with gl*.  OpenGL doesn't have an "INTERFACE"
  // in the OOP sense (but that set of gl* C functions
  // is still an 'interface' though, its just a very
  // different way of creating one!)

  // Anyway, on with it.
  #pragma endregion
};

///////////////////////////
// GLOBALS
// declare one struct Globals called g;
Globals g;
//
///////////////////////////


///////////////////////////
// FUNCTION PROTOTYPES
// Windows app functions.  If need help
// understanding these, see MostBasicWindow
// and FastWindowsProgram
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam );
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow );

inline bool CHECK( HRESULT hr, char * msg, bool stop=true ) ;  // checks for errors on the HR passed.

bool initD3D() ;         // function to initialize the BEAST that is Direct3D9
void printSystemInfo() ; // function that prints some system info.  can ignore if not interested
void draw() ;            // drawing function containing Direct3D drawing calls

//
///////////////////////////


/// If there was an error, the ErrorString is printed out for you to see at the console.
inline bool CHECK( HRESULT hr, char * msg, bool stop )
{
  if( FAILED( hr ) )
  {
    printf( "%s. %s:  %s\n",
            msg, DXGetErrorString9A( hr ), DXGetErrorDescription9A( hr ) ) ;

    // Pause so we can see the error and deal with it.
    if( stop )  system("pause") ;

    return false ;
  }
  
  else
    return true ;


}

///////////////////////////
// FUNCTION IMPLEMENTATIONS
/// Initializes Direct3D9.  Returns true on success.
bool initD3D()
{
  // start by nulling out both pointers:
  g.d3d = 0 ;
  g.gpu = 0 ;
  
  // Create the IDirect3D9 (d3d) object now.  We need the d3d object
  // in order to be able to create the IDirect3DDevice9 interface.
  // We can also use the d3d object to find out additional information
  // about the system we are on (such as the number of displays it has, etc).

  // [[ I know, I know.  If you think its confusing/stupid to have
  // two separate Direct3D9 things both starting with I, 
  // just bear with it because you'll see that these two objects
  // will do VERY different things, and you'll see it does
  // make a whole lot of sense to separate them out into two things.
  // Granted, they could have been named a little more distinctly,
  // but whaddya gonna do... ]]
  
  // So, the IDirect3D9 object (variable 'd3d') is the "interface"
  // or means by which we will access the Direct3D9
  // beast
  
  // And the IDirect3DDevice9 (called 'gpu') is THE
  // variable that IS OUR PROGRAMMATIC HANDLE TO THE GPU and
  // we will use it A LOT to send down triangles and stuff to the
  // gpu to be drawn.

  // we'll be using 'gpu' a lot more than 'd3d'.

  // So now, to create our 'd3d' interface.
  // Remember, FROM this interface will come
  // our 'gpu' interface.

  // Direct3DCreate9
  g.d3d = Direct3DCreate9( D3D_SDK_VERSION ) ;  // Always use D3D_SDK_VERSION

  if( g.d3d == NULL )
  {
    // DEVICE CREATION FAILED!!!! OH NO!!!
    puts( "Oh.. PHOOEY!!!!!  Device creation FAILED!!! WHAT NOW???\n" ) ;
    return false ;
  }



  // Ok, if we get here without returning, it means device creation succeeded.
  puts( "Device creation SUCCESS!!!!\nWe're in business now, son..\n" ) ;
  
  // Next we'll just print a few details about the system
  // just because we can..
  // Note how we use PURELY the 'd3d' object
  // to do this (NOT the 'gpu' device, which hasn't
  // even been created yet!)
  ///// printf( "I will now tell you some \nthings ABOUT your system.\n" );
  ///// printSystemInfo(); // BOOOOORING.
  


  puts( "Ok, Now to CREATE the 'gpu' device!!" ) ;
  
  // First, create the D3DPRESENT_PARAMETERS structure.
  // This structure will basically "explain" to the
  // IDirect3D9 interface EXACTLY WHAT we want the
  // GPU rendering surface to look like once
  // it has been created.



  // D3DPRESENT_PARAMETERS structure
  D3DPRESENT_PARAMETERS pps = { 0 } ;  // Start structure all 0'd out.

  // We're using a windowed mode, NOT fullscreen in this
  // example.  Its really annoying to program little test
  // apps in fullscreen mode.  Also when using windowed mode
  // we don't have to (in fact, we should not) specify
  // some of the other parameters, such as the refresh rate.
  pps.Windowed = true ;

  // How many backbuffers do we want?  One.
  pps.BackBufferCount = 1 ;

  // This one's interesting
  // Backbuffering.  Imagine Leonardo Davinci doing
  // a live animation for you.

  // Imagine he stood in front of the canvas,
  // and blazingly quickly, painted a live scene for you.
  // Then to make the animation effect happen, he'd have
  // to erase the canvas (paint over it in all white?) then
  // paint over that white the next frame.

  // Not that great for watching an "animation!"  even if
  // he moved blazing fast, it'd still be "flickery" having
  // to "see" the canvas get all cleared out, then see each
  // shape get drawn on.

  // So instead, Davinci has a better idea.  He will
  // use TWO canvases.  He will draw to the canvas
  // in a HIDDEN place, where you can't see it.

  // When he's done painting hte first frame, BAM,
  // he slams it in your face and you can see it.
  // He then takes a SECOND canvas, and paints to it
  // blazing fast, what should be the next frame you see.
  // Then, BAM, he slams that second canvas right in your face,
  // where you can see it.  He then quietly pulls away
  // that first canvas that had the first frame on it
  // (which you can't see anymore, because you're looking
  // at the SECOND canvas he just slammed in your face),
  // and quickly paints the NEXT (3rd) frame onto it.
  // Then, BAM, he slams that first canvas in your face
  // again, but now it has the 3rd frame on it.  He then
  // takes the SECOND canvas, and draws the 4th frame on it...

  // Do you see what's happening here?  Read it again
  // if not... the whole point is to give a MUCH smoother
  // and continuous presentation of image frames.  If you
  // didn't use a backbuffer, then the animation presented
  // would look all awful and flickery and horrible because
  // you'd basically be WATCHING the gpu do its drawing work,
  // instead of looking at nice finished product painted scenes instead.

  // Swap chains
  pps.SwapEffect = D3DSWAPEFFECT_DISCARD ; // You start with
  // 2 buffers, one that displays what you're currently
  // looking at (the "frontbuffer") and
  // one that is hidden from you (the "backbuffer").
  
  // Basically FLIP means that
  // d3d should DRAW to the BACKBUFFER first, then
  // when its done doing that, it should BAM, slam
  // that backbuffer in your face, so you can see it.
  // The former front buffer, is then DISCARDED.

  // SO, the former "backbuffer" is NOW the FRONTBUFFER.
  // And the former front buffer, we will treat
  // as the NEW "backbuffer".

  // SO draw to the OTHER buffer now (which is considered
  // as the backbuffer at the moment), when you're done,
  // BAM, slam that backbuffer in the user's face
  // (so again, the former "backbuffer" has flipped
  // and become the frontbuffer).

  // The OTHER way to do this is to DRAW TO
  // the backbuffer, then to COPY OUT the backbuffer
  // in its entirety to the front buffer.  That's
  // less efficient though, for obvious reasons
  // (copying about 2,000,000 pixels has gotta take
  // at least some time!).  Discard is nice, but
  // using it REQUIRES that you completely update
  // ALL the pixels of the backbuffer before presenting it
  // (because there's NO TELLING what will happen to
  // the frontbuffer once you've "discarded" it!
  // Microsoft says "we might do anything to it..."
  // The Clear() operation is sufficient to touch every
  // pixel on the buffer, so as long as you're calling
  // Clear() every frame, there's nothing to worry about.
  // You'll see Clear() in use a bit later in this tutorial.)

  // Pixel format to use on the backbuffer.
  pps.BackBufferFormat = D3DFMT_UNKNOWN ;   // So, this doesn't mean
  // we don't KNOW the pixel format we want.. its more
  // like saying "D3DFMT_CHOOSE_IT_FOR_ME!"
  
  // What really happens is "color conversion is done by the hardware"

  // But you can think of it as D3D should picking
  // the appropriate pixel format to use.

  
  
  // Now, we WANT Direct3D to create and manage
  // a depth buffer for this surface.  That means
  // if we draw two triangles and one is "closer"
  // than the other, then the "closer" one should
  // be drawn on top of the "further" one.  That's
  // achieved using the depth buffer
  pps.EnableAutoDepthStencil = true ;
  // Now we have to say "how deep" is the depth buffer.
  // Kind of.  How precise are the depth values?
  // The more bits you use, the more "slots of depth"
  // your depth buffer can handle.  If your depth buffer
  // used 2 bits, it might be able to handle 4 levels
  // of depth.  With 16 bits, there's a lot more levels of
  // depth.  Having many different levels of depth is
  // really important because if two objects are
  // like thought by the gpu to be at the exact same
  // "depth", then there will be "z-fighting"
  // z-fighting sample1
  // Apparently some users experienced problems
  // with z-fighting when playing GTA 4 on ATI hardware.

  // We choose a fairly "deep" pixel format (16 bits)
  // Could also choose 24 bits.
  pps.AutoDepthStencilFormat = D3DFMT_D16 ;

  // Finally, make the gpu device.
  HRESULT hr = g.d3d->CreateDevice(
    
    D3DADAPTER_DEFAULT, // primary display adapter
    D3DDEVTYPE_HAL,     // use HARDWARE rendering (fast!)
    g.win.hwnd,
    D3DCREATE_HARDWARE_VERTEXPROCESSING,
    &pps,
    &g.gpu  // THIS IS where you get
    // the ACTUAL return value (the
    // GPU device!) from this function.
    // Because this function has its
    // return value as an HRESULT,
    // the return value is used to
    // indicate success or failure.

  ) ;


  if( !CHECK( hr, "OH NOS!! I could not initialize Direct3D!  Bailing...\n" ) )
  {
    return false ;
  }



  // Successfully created direct3d9 devices
  printf( "WHOO!  We SUCCESSFULLY created the Direct3D9 GPU device\n" ) ;
  


  return true ;
}


////////////////////////
// DRAWING FUNCTION
void draw()
{
  HRESULT hr ;

  #pragma region clear
  // First, we will clear the backbuffer.  This is a VERY general use
  // function and has way more capabilities than we care to use
  // at the moment.  For example, you can choose to clear little
  // sub-rectangles of the screen ONLY instead of clearing the whole
  // screen with the first 2 params.  we're not interested in that though,
  // we just wanna clear the whole screen.

  // IDirect3DDevice9::Clear()
  hr = g.gpu->Clear(

    0, // NUMBER of sub rectangles to clear.  We set to 0 because
       // we don't want to even clear any subrectangles.. we just
       // want to clear the WHOLE thing!

    0, // you can choose to clear only a sub-region of the
       // backbuffer.  But we wanna clear the WHOLE back buffer!
       // so we pass 0 here and d3d will automatically clear the WHOLE THING for us.

    // D3DCLEAR
    D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER /* | D3DCLEAR_STENCIL */,  // next is weird, but
    // here we specify WHAT exactly we want cleared.  Because a 3d
    // buffer is actually made of several LAYERS (color layer,
    // and depth layer, and stencil layer), you choose
    // what exactly what you want cleared.  If you weren't
    // doing 3d graphics for example (you used direct3d9 to draw
    // 2d graphics, which you can!) you would have no need for
    // the depthbuffer.  So you could save a bit of time
    // (clearing a buffer is a relatively expensive operation)
    // by NOT clearing the buffers you aren't going to use.
    
    // In this example we are NOT using the stencil buffer,
    // but we ARE using the color BUFFER ITSELF (like color values)
    // hence specification of D3DCLEAR_TARGET,
    // and we ARE using the depth buffer (D3DCLEAR_ZBUFFER)
    // but we are NOT using the stencil buffer (hence me omitting
    // D3DCLEAR_STENCIL).  There's no sense in clearing out a
    // buffer that's not in use.  That's like vaccumming a room
    // that's just been vaccummed squeaky clean.
    // If you're not going to be picking up any extra dirt,
    // cleaning it again is really just a waste of time and energy.
    
    D3DCOLOR_ARGB( 255, 125, 25, 237 ),  // The color to clear
    // the backbuffer out to.
    
    1.0f, // value to clear the depth buffer to.  we clear
    // it to 1.0f because 1.0f means ("furthest away possible
    // before being out of range")

    // So we clear every value in the depth buffer to this
    // value so when something is rendered that is in view range,
    // it will definitely be closer than 1.0f!
    // (0.0f means right in our faces).
    
    0  // value to clear the stencil buffer to.  since we
    // chose NOT to clear the stencil buffer (omitted
    // D3DCLEAR_STENCIL from above), this value is
    // actually going to be ignored.
    
  ) ; 
  CHECK( hr, "Clear FAILED!" ) ;

  #pragma endregion

  #pragma region set up the camera

  // First we start by setting up the viewport.
  // the viewport "Defines the window dimensions of
  // a render-target surface onto which a 3D volume projects."
  D3DVIEWPORT9 viewport ;

  // Very clear explanations of each (and advice! :) )
  // of these members are on msdn.
  viewport.X = 0 ;
  viewport.Y = 0 ;
  viewport.Width = g.win.width ;
  viewport.Height = g.win.height ;
  viewport.MinZ = 0.0f ;
  viewport.MaxZ = 1.0f ;

  g.gpu->SetViewport( &viewport ) ;


  // Technically we don't need to set the viewport here
  // BUT you can use viewport setting to draw "picture in picture" -

  // Form the 

  // Set projection matrix
  D3DXMATRIX projx ;
  
  // Create a perspective matrix
  D3DXMatrixPerspectiveFovRH( &projx, PI/4, (float)g.win.width/g.win.height, 1.0f, 1000.0f ) ;
  
  // set
  g.gpu->SetTransform( D3DTS_PROJECTION, &projx ) ;

  // Create the view matrix
  D3DXMATRIX viewx ;

  D3DXVECTOR3 eye( 4, 2, 4 ) ;
  D3DXVECTOR3 look( 0, 0, 0 ) ;
  D3DXVECTOR3 up( 0, 1, 0 ) ;
  D3DXMatrixLookAtRH( &viewx, &eye, &look, &up ) ;
  g.gpu->SetTransform( D3DTS_VIEW, &viewx ) ;
  #pragma endregion

  // Preparing to draw
  // FVF.  WTF is an FVF?
  // MSDN:  "Flexible Vertex Format Constants, or FVF codes,
  //   are used to describe the contents of vertices
  //   interleaved in a single data stream that will
  //   be processed by the fixed-function pipeline."

  // FVF stands for "FLEXIBLE VERTEX FORMAT".  If you're familiar with
  // OpenGL, this is a completely (but some might say a little bit better..)
  // way of allowing a person to say WHAT data each vertex has tagged along
  // with it.

  // So let's tell Direct3D what data exactly each VERTEX 
  // will have.  A position?  A color?  A normal?  A texture
  // coordinate?  What do you WANT TO specify for each vertex.

  hr = g.gpu->SetFVF(
    
    D3DFVF_XYZ  // THe most OBVIOUS (and most important?) aspect of a VERTEX is
    // that it have a POSITION, XYZ.  Specifying that our vertex format
    // includes an XYZ position
    
    | D3DFVF_DIFFUSE // We also specify a diffuse color for each vertex.
    
  ) ;
  CHECK( hr, "SetFVF FAILED!" ) ;
  
  // So there we have it.  We just told d3d to expect
  // to get vertices that each have an XYZ coordinate
  // AND a color specified for them.

  

  // SO NOW, before DRAWING anything, we have to 
  // do 2 more things:
  //   #1)  Create a D3DVERTEXELEMENT9 structure
  //       which will represent our vertex format.

  //   #2)  Create an array of vertices to draw!
  
  //   #3)  Just draw it!

  // #1)  This part is a bit confusing at first,
  // but if you read through it carefully, it should
  // make sense.

  // What we need to do here is create a SPECIAL
  // kind of structure called a "VERTEX DECLARATION"
  // This VertexDeclaration will MATCH UP with
  // the "FVF" format that we set up just a couple
  // of lines ago.  Read on!

  // In the specification, we actually have to
  // obey the FVF mapping
  // on msdn.

  /////////////
  #pragma region // <position vertex element decl>
  // IDirect3DVertexDeclaration9
  
  // OK in the FVF above, we promised d3d that
  // EACH AND EVERY vertex would have a POSITION
  // and a COLOR.

  // So we declare and set up TWO D3DVERTEXELEMENT9
  // structures which explain to d3d the EXACT format
  // and nature of each vertex -

  // IS this a bit redundant?  Kind of, yes.  Getting
  // on with it.

  D3DVERTEXELEMENT9 pos ;
  
  // Here is where we say that this part
  // of the vertex will specify a POSITION
  // in space.
  pos.Usage = D3DDECLUSAGE_POSITION ;

  // This part makes sense if you understand
  // that sometimes, you want to send MORE THAN
  // one position coordinate for each vertex.
  // If that makes less sense, then think about
  // sending down more than one COLOR for each
  // vertex, for some type of funky color blending.
  pos.UsageIndex = 0 ;

  pos.Stream = 0 ; // Vertex shaders have a concept
  // of "stream".
  
  // So what's a "stream" you ask?
  // This really is QUITE an advanced topic, so my honest advice
  // to you is to completely ignore the below unless you 
  // REALLY want to know what streams are.

  // <streams>
  // There is a concept in GPU programming called "shader instancing".
  // Shader instancing is when you specify a model's geometry once,
  // then draw that same model like a million times.

  // 

  // So, you specify the model vertex data with POSITIONS on
  // channel 0, for example.  Then you specify a bunch of
  // positions on channel 1 (1,000,000 positions in your game world,
  // or something) that describe places to draw ALL the vertices
  // that are on channel 0.

  // So its really quite complicated and hard to understand.
  // Channels are like TV -- like the TV station sends like,
  // 500 channels down to your TV in parallel (hey, pretend
  // they do), the vertex data you send down to the GPU
  // all goes down to the GPU in parallel, work at drawing the same thing, kind of.
    
  // Your tv can tune into one channel at a time only, and the GPU
  // will actually tune into ALL the channels when drawing..
  // </streams>  See http://msdn.microsoft.com/en-us/library/bb147299(VS.85).aspx for more detail.
    
  // UH, where were we?  Next, we specify the actual data type of
  // the position data.

  pos.Type = D3DDECLTYPE_FLOAT3 ;  // "Vertices will use
  // 3 floats to specify their position in space".
  
  // If you are familiar with GPU datatypes and HLSL,
  // this would correspond directly with the "float3"
  // datatype in the vertex shader.
  
  // If you don't know what a vertex shader is,
  // just suffice it to say, that all this means
  // is 3 floats will be used for the POSITION
  // of the vertex.
  
  // Next we set the "offset":

  pos.Offset = 0 ;

  // In the Vertex STRUCT that WE defined,
  // this is the byte offset from the start
  // of the struct where D3D SHOULD expect
  // to find this data.
  
  // Using default method.
  pos.Method = D3DDECLMETHOD_DEFAULT ; // here's more info
  
  #pragma endregion // </position vertex element decl>
  /////////////


  /////////////
  #pragma region // <color vertex element decl>
  // Next we declare the vertex element that
  // will represent the DIFFUSE COLOR component.
  D3DVERTEXELEMENT9 col;
  
  col.Usage = D3DDECLUSAGE_COLOR ; // its a color
  col.UsageIndex = 0 ; // COLOR0
  col.Stream = 0 ;

  col.Type = D3DDECLTYPE_D3DCOLOR ; // UNFORTUNATELY you MUST
  // chose D3DDECLTYPE_D3DCOLOR when using COLOR0
  // or COLOR1.  If you write your own shader,
  // then you can use a FLOAT4 color.
  // If you try and do that, you will get
  // [5052] Direct3D9: Decl Validator: X254: (Element Error) (Decl Element [2])
  // Declaration can't map to fixed function FVF because color0/color1
  // cannot have type other than D3DDECLTYPE_D3DCOLOR
  // in the d3d extended debug output.

  
  // NEXT, the OFFSET.  The offset is
  // 3*sizeof(float) because the 'POSITION'
  // comes first and takes up 3 floats as we
  // specified above.
  col.Offset = 3*sizeof( float ) ;
  col.Method = D3DDECLMETHOD_DEFAULT ;
  #pragma endregion // </color vertex element decl>
  /////////////
  

  /////////////
  #pragma region create and set vertex declaration
  // Now put the two D3DVERTEXELEMENT9's into
  // an array and create the VertexDeclaration:
  D3DVERTEXELEMENT9 vertexElements[] =
  {
    pos,
    col,

    // VERY IMPORTANT!  D3D doesn't konw
    // HOW MANY elements you will be specifying
    // in advance, so you TELL IT by passing
    // this SPECIAL D3DVERTEXELEMENT9 object
    // which is basically just like the null
    // terminator at the end of C string.
    D3DDECL_END()
  } ;
  
  IDirect3DVertexDeclaration9 * Vdecl ;

  // Now register in the "vertexElements" array
  // we just created above into the decl
  hr = g.gpu->CreateVertexDeclaration( vertexElements, &Vdecl ) ;
  CHECK( hr, "CreateVertexDeclaration FAILED!" ) ;

  // Now SET IN that vertex declaration into the GPU
  hr = g.gpu->SetVertexDeclaration( Vdecl ) ;
  CHECK( hr, "SetVertexDeclaration FAILED!" ) ;
  #pragma endregion

  #pragma region set render states
  // FINALLY, last thing before drawing, we
  // have to set the renderstate to USE
  // the DIFFUSE component to determine the
  // color of each vertex
  // Per-Vertex Color State
  hr = g.gpu->SetRenderState( D3DRS_COLORVERTEX, TRUE ) ;
  CHECK( hr, "SetRenderState( COLORVERTEX ) FAILED!" ) ;

  
  // Turn LIGHTING off.  TRUE to enable Direct3D lighting,
  // or FALSE to disable it. The default value is TRUE.
  // __Only vertices that include a vertex normal are properly lit;
  // vertices that do not contain a normal ___employ a dot product of 0___
  // in all lighting calculations.__!

  // So if you don't disable lighting, what will actually happen is,
  // since we don't specify normals for any of our vertices,
  // they will all have a normal vertex of the 0 vector, so
  // the result is they will all be completely black.
  hr = g.gpu->SetRenderState( D3DRS_LIGHTING, FALSE ) ;
  CHECK( hr, "Lighting off" ) ;


  // Turn backface culling off.  Its good to turn this off
  // when starting out because triangles that you wind
  // ccw are discarded if this is on.
  // http://msdn.microsoft.com/en-us/library/bb204882(VS.85).aspx
  hr = g.gpu->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ) ;
  CHECK( hr, "Culling off" ) ;

  #pragma endregion

  #pragma region declare a vertex structure, create verts
  // OK?? WHEW!!  THAT was a lot of work!
  // Now let's DRAW SOMETHING!!  First,
  // we have to create a STRUCT which
  // will match up the vertex declaration
  // we specified above.

  // Practically you'd probably want this struct
  // to be declared in global space, but its declared
  // inline here just to preserve the
  // flow of information for you.

  struct Vertex 
  {
    float x,y,z ;
    DWORD color ;

    // Ctor starts you at origin in black
    // with alpha (opacity) set to 100%
    Vertex()
    {
      x=y=z = 0.0f;
      color = D3DCOLOR_XRGB( 0,0,0 ) ;
    }

    // Ctor.
    Vertex( float ix, float iy, float iz,
      unsigned char ir, unsigned char ig, unsigned char ib )
    {
      x=ix;y=iy;z=iz;
      color = D3DCOLOR_XRGB( ir, ig, ib ) ;
    }

    // Ctor that lets you pick alpha
    Vertex( float ix, float iy, float iz,
      unsigned char ir, unsigned char ig, unsigned char ib, unsigned char ALPHA )
    {
      x=ix;y=iy;z=iz;
      color = D3DCOLOR_ARGB( ALPHA, ir, ig, ib ) ;
    }
  } ;

  // Now create an array full of vertices!
  Vertex verts[] = {

    // Red vertex @ ( -1, 0, 0 )
    Vertex( -1, 0, 0, 255, 19, 0 ),

    // Green vertex @ ( 0, 1, 0 )
    Vertex(  0, 1, 0, 0, 255, 0 ),

    // Blue vertex @ ( 1, 0, 0 )
    Vertex(  1, 0, 0, 0, 0, 255 )

  } ;

  float axisLen = 2.0f ;
  Vertex axis[] = {

    // x-axis is red
    Vertex( -axisLen, 0, 0, 255, 0, 0 ),
    Vertex( +axisLen, 0, 0, 255, 0, 0 ),

    // y-axis green
    Vertex( 0, -axisLen, 0, 0, 255, 0 ),
    Vertex( 0, +axisLen, 0, 0, 255, 0 ),

    // z-axis blue
    Vertex( 0, 0, -axisLen, 0, 0, 255 ),
    Vertex( 0, 0, +axisLen, 0, 0, 255 )

  } ;

  #pragma endregion

  #pragma region ACTUALLY __draw__
  // IDirect3DDevice9::BeginScene()
  // You must call BeginScene() before you start drawing anything.
  hr = g.gpu->BeginScene() ;
  CHECK( hr, "BeginScene FAILED!" ) ;

  hr = g.gpu->DrawPrimitiveUP( D3DPT_TRIANGLELIST, 1, verts, sizeof( Vertex ) ) ;
  CHECK( hr, "DrawPrimitiveUP FAILED!" ) ;

  hr = g.gpu->DrawPrimitiveUP( D3DPT_LINELIST, 3, axis, sizeof( Vertex ) ) ;
  CHECK( hr, "DrawPrimitiveUP FAILED!" ) ;

  float pointSize = 8.0f ;

  // A DWORD is 4 bytes (a WORD is 2 bytes).
  // So, what we need to do is basically cast
  // pointSize to (DWORD).  Its a bit complicated
  // about how you actually do it, so I made a macro for it.
  // The general idea is you need to "trick" SetRenderState
  // into taking your float value.. SetRenderState "thinks"
  // its a DWORD, while its actually a float.. and D3D
  // internally somehow gets it and knows to treat it as a float.
  // Kind of clunky, eh, but what can you do.
  g.gpu->SetRenderState( D3DRS_POINTSIZE, CAST_AS_DWORD( pointSize ) ) ;
  
  // Draw points at end of axis.
  Vertex points[] = {
    Vertex( axisLen, 0, 0, 255, 0, 0 ),
    Vertex( 0, axisLen, 0, 0, 255, 0 ),
    Vertex( 0, 0, axisLen, 0, 0, 255 ),
  } ;
  hr = g.gpu->DrawPrimitiveUP( D3DPT_POINTLIST, 3, points, sizeof( Vertex ) ) ;
  CHECK( hr, "DrawPrimitiveUP FAILED!" ) ;

  // endscene, and present
  // IDirect3DDevice9::EndScene()
  // You must call EndScene() to signify to the gpu that
  // you are finished drawing.  Must pair up with
  // a BeginScene() call that happened earlier.
  hr = g.gpu->EndScene() ;
  CHECK( hr, "EndScene FAILED!" ) ;

  // And finally, PRESENT what we drew to the backbuffer
  g.gpu->Present( 0, 0, 0, 0 ) ;
  #pragma endregion



}




// function that prints some system info.  can ignore this part
// if you are not interested in it.
void printSystemInfo()
{
  UINT numAdapters = g.d3d->GetAdapterCount() ;
  printf( "\n\n* * * * System information * * * *\n" ) ;
  printf( "Owner name:  Dorky Dorkinson (haha, just kidding, little easter egg there..)\n" ) ;
  printf( "Ok, the rest of this information IS real!\n" ) ;
  
  printf( "You have %d adapters\n  * (this number is bigger than 1 if you have dualview)\n", numAdapters ) ;

  // Scroll through all adapters and print some info about each
  for( int i = 0 ; i < numAdapters ; i++ )
  {
    printf( "\n\n-- ADAPTER #%d --\n", i ) ;
    printf( "On monitor #%d\n", g.d3d->GetAdapterMonitor( i ) ) ;

    // Object into which display mode info will be saved
    // by GetAdapterDisplayMode
    D3DDISPLAYMODE displayMode ;
    g.d3d->GetAdapterDisplayMode( i, &displayMode ) ;

    printf( "Has Format=%d Height=%d Width=%d RefreshRate=%d\n", displayMode.Format, displayMode.Height, displayMode.Width, displayMode.RefreshRate ) ;
    printf( "  * (format refers to the D3DFMT_ pixel mode.. e.g. 22=D3DFMT_X8R8G8B8 which is 24 bit color)\n" ) ;
    
    D3DADAPTER_IDENTIFIER9 id ; // Will hold info about the adapter
    // after call to GetAdapterIdentifier

    g.d3d->GetAdapterIdentifier( i, 0, &id ) ;
    // At this point you can see how WEIRD the API gets.
    // All I want is the adapter identifier, and here MSDN
    // says the API offers to "connect to the Internet
    // and download new MS Windows Hardware Quality Labs certificates."
    // Holy cow.  I don't want to do THAT.  So leave the flag at 0.
    
    // There's PLENTY of info here we don't care about,
    // but some of it is interesting!!  I've printed
    // only the most interesting members, leaving parts
    // like the GUID out.
    printf( "<device driver info>\n" ) ;
    printf( "  Description: %s\n  Device Id: %d\n  Device name: %s\n  Driver: %s\n",
      id.Description, id.DeviceId, id.DeviceName, id.Driver ) ;
    printf( "</device driver info>\n" ) ;
    

    UINT modeCount ;
    
    // I guess this next part just shows.. how FEW modes are
    // actually supported on a GPU.. I have an NVIDIA 8800GTS,
    // and it only supports 28 modes on D3DFMT_X8R8G8B8,
    // and 28 modes on D3DFMT_R5G6B5.  The rest, 0 modes are
    // reported as supported!

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_R8G8B8 ) ;
    printf( "D3DFMT_R8G8B8   %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A8R8G8B8 ) ;
    printf( "D3DFMT_A8R8G8B8 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_X8R8G8B8 ) ;
    printf( "D3DFMT_X8R8G8B8 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_R5G6B5 ) ;
    printf( "D3DFMT_R5G6B5   %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_X1R5G5B5 ) ;
    printf( "D3DFMT_X1R5G5B5 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A1R5G5B5 ) ;
    printf( "D3DFMT_A1R5G5B5 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A4R4G4B4 ) ;
    printf( "D3DFMT_A4R4G4B4 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_R3G3B2 ) ;
    printf( "D3DFMT_R3G3B2   %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A8 ) ;
    printf( "D3DFMT_A8       %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A8R3G3B2 ) ;
    printf( "D3DFMT_A8R3G3B2 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_X4R4G4B4 ) ;
    printf( "D3DFMT_X4R4G4B4 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A2B10G10R10 ) ;
    printf( "D3DFMT_A2B10G10R10 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A8B8G8R8 ) ;
    printf( "D3DFMT_A8B8G8R8 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_X8B8G8R8 ) ;
    printf( "D3DFMT_X8B8G8R8 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_G16R16 ) ;
    printf( "D3DFMT_G16R16   %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A2R10G10B10 ) ;
    printf( "D3DFMT_A2R10G10B10 %d modes supported\n", modeCount ) ;

    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_A16B16G16R16 ) ;
    printf( "D3DFMT_A16B16G16R16 %d modes supported\n", modeCount ) ;






    // This mode is the MOST LIKELY TO be supported on your machine.
    modeCount = g.d3d->GetAdapterModeCount( i, D3DFMT_X8R8G8B8 ) ;
    for( int j = 0 ; j < modeCount; j++ )
    {
      g.d3d->EnumAdapterModes( i, D3DFMT_X8R8G8B8, j, &displayMode ) ;

      printf( "For format=%d (D3DFMT_X8R8G8B8) Height=%d Width=%d RefreshRate=%d is SUPPORTED\n", displayMode.Format, displayMode.Height, displayMode.Width, displayMode.RefreshRate ) ;

    }


    // At this point you're thinking, "HEY!!  But i'm SURE my gpu supports
    // alpha blending!  Why are all the modes like A8R8G8B8 NOT supported!?"
    // My best stab at this is it makes no sense to DISPLAY something with
    // an alpha component still in it.  For the final image that gets displayed --
    // the alphas should already have been blended -- present day
    // monitors can only display RGB, they don't have the ability to
    // "go transparent".  Maybe one day when we work with those enormous
    // glass screens that people work with in the movies -- kinda like this one:
    // http://business.timesonline.co.uk/multimedia/archive/00339/screen-385_339034a.jpg
    // then having an ALPHA component on the display WOULD make sense.  But for
    // now, all monitors are completely opaque, and they only display colors
    // RGB.  So an alpha component makes no sense on the display itself.
    // That's why displays don't support that display mode.



    // K, FINALLY we get to the device's capabilities.
    // I love how DirectX lets you "reflect" on what
    // the hardware is in fact capable of.  Its nice.

    D3DCAPS9 caps ;
    g.d3d->GetDeviceCaps( i, D3DDEVTYPE_HAL, &caps ) ;

    // Now we have the capabilities of the device.
    // Printing all of them would be meaningless,
    // but if you want to inspect some of them, just
    // use the debugger.

  }
}





int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow )
{
  //////////////////
  // First we'll start by saving a copy of
  // the hInstance parameter inside our
  // "glob" of globals "g":
  g.win.hInstance = hInstance;
  // In case we need it later, we'll have it
  // with firsthand easy access.

  #pragma region part 0 - attach a console
  // Attach a console
  AllocConsole();
  AttachConsole( GetCurrentProcessId() ) ;
  freopen( "CON", "w", stdout ) ; // redirect stdout to console
  freopen( "CON", "w", stderr ) ; // redirect stderr to console

  // Move the console over to the top left
  g.win.hConsole = GetConsoleWindow();
  MoveWindow( g.win.hConsole, 0, 0, 400, 400, true ) ;

  printf( "* * Computer Program Begin * *\n" ) ;
  #pragma endregion

  #pragma region part 1 - create a window
  // The next few lines you should already
  // be used to:  create a WNDCLASSEX
  // that describes the properties of
  // the window we're going to soon create.
  // A.  Create the WNDCLASSEX
  WNDCLASSEX wcx = { 0 } ;
  wcx.cbSize = sizeof( WNDCLASSEX );
  wcx.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
  wcx.hCursor = LoadCursor( NULL, IDC_ARROW );
  wcx.hIcon = LoadIcon( NULL, IDI_APPLICATION );
  wcx.hInstance = hInstance;
  wcx.lpfnWndProc = WndProc;
  wcx.lpszClassName = TEXT("Philip");
  wcx.lpszMenuName = 0;
  wcx.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;

  // Register that class with the Windows O/S..
  RegisterClassEx( &wcx );

  /////////////////
  // Ok, AT THIS POINT, we'd normally
  // just go ahead and call CreateWindow().
  // And we WILL call CreateWindow(), but
  // there is something I must explain to
  // you first.  That thing is the RECT structure.

  /////////////////
  // RECT:
  //
  // A RECT is just a C struct meant to represent
  // a rectangle.
  //
  // The RECT structure WILL DESCRIBE EXACTLY WHERE
  // AND HOW WE WANT OUR WINDOW TO APPEAR WHEN WE
  // CREATE IT.
  //
  //         TOP
  //       --------
  //       |      |
  // LEFT  |      | RIGHT
  //       --------
  //        BOTTOM
  //
  // So, what we do is, we create the RECT
  // struct for our window as follows:
  RECT rect;
  SetRect( &rect, 420,  // left
    25,  // top
    420 + 800, // right
    25  + 600 ); // bottom

  // Save width and height off.
  g.win.width = rect.right - rect.left;
  g.win.height = rect.bottom - rect.top;

  // Adjust it.
  DWORD windowStyle = WS_OVERLAPPEDWINDOW ; // typical features of a normal window
  DWORD windowExStyle = WS_EX_TOPMOST ; // I want the window to be topmost

  AdjustWindowRectEx( &rect, windowStyle, false, windowExStyle );

  // AdjustWindowRect() expands the RECT
  // so that the CLIENT AREA (drawable region)
  // has EXACTLY the dimensions we specify
  // in the incoming RECT.

  // If you didn't just understand that, understand
  // this:  "you have to call AdjustWindowRect()",
  // and move on.  Its not THAT important, but its
  // good for the performance of your app.

  ///////////////////
  // NOW we call CreateWindow, using
  // that adjusted RECT structure to
  // specify the width and height of the window.
  g.win.hwnd = CreateWindowEx(
    windowExStyle,
    TEXT("Philip"),
    TEXT("TIGER-DIRECT3D WINDOW!"),
    windowStyle,
    rect.left, rect.top,  // adjusted x, y positions
    rect.right - rect.left, rect.bottom - rect.top,  // adjusted width and height
    NULL, NULL,
    hInstance, NULL);

  // check to see that the window
  // was created successfully!
  if( g.win.hwnd == NULL )
  {
    FatalAppExit( NULL, TEXT("CreateWindow() failed!") );
  }

  // and show.
  ShowWindow( g.win.hwnd, iCmdShow );
  #pragma endregion

  #pragma region part 2 - initialize direct3d9

  // JUMP to the initD3D() method.
  if( !initD3D() )
  {
    FatalAppExit( 0, TEXT("SORRY!!!  DEVICE CREATION FAILED!!! YOU LOSE, WITHOUT EVEN PLAYING THE GAME!!!" ) ) ;
  }
  
  #pragma endregion

  #pragma region message loop
  MSG msg;

  while( 1 )
  {
    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
      if( msg.message == WM_QUIT )
      {
        break;
      }
      else
      {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
      }
    }

    // 3.  DRAW USING Direct3D.
    // This region right here is the
    // heart of our application.  THE MOST
    // execution time is spent just repeating
    // this draw() function.
    draw();

  }
  #pragma endregion

  //////////////
  // clean up
  #pragma region clean up

  // Release COM objects.  
  // What's SAFE_RELEASE()?  Well, lots of people use
  // if( pointer ) pointer->Release() ; to guard
  // against null pointer exceptions.
  
  // Like the MS examples do, I've defined
  // SAFE_RELEASE at the top of this file and
  // I'm using it here.  All it does is
  // make sure the pointer is not null before releasing it.
  SAFE_RELEASE( g.gpu ) ;
  SAFE_RELEASE( g.d3d ) ;

  #pragma endregion

  // and a cheesy fade exit
  AnimateWindow( g.win.hwnd, 200, AW_HIDE | AW_BLEND );

  printf( "* * This Computer Program Has Ended * *\n" ) ;
  
  return msg.wParam;
}

////////////////////////
// WNDPROC
// Notice that WndProc is very very neglected.
// We hardly do anything with it!  That's because
// we do all of our processing in the draw()
// function.
LRESULT CALLBACK WndProc(   HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam )
{
  switch( message )
  {
  case WM_CREATE:
    Beep( 50, 10 );
    return 0;
    break;

  case WM_PAINT:
    {
      HDC hdc;
      PAINTSTRUCT ps;
      hdc = BeginPaint( hwnd, &ps );
      // don't draw here.  would be waaay too slow.
      // draw in the draw() function instead.
      EndPaint( hwnd, &ps );
    }
    return 0;
    break;

  case WM_KEYDOWN:
    switch( wparam )
    {
    case VK_ESCAPE:
      PostQuitMessage( 0 );
      break;
    default:
      break;
    }
    return 0;

  case WM_SIZE:
    {
      int width = LOWORD( lparam ) ;
      int height = HIWORD( lparam ) ;
      printf( "RESIZED TO width=%d height=%d\n", width, height ) ;
    }
    break;

  case WM_DESTROY:
    PostQuitMessage( 0 ) ;
    return 0;
    break;
  }

  return DefWindowProc( hwnd, message, wparam, lparam );
}


/*
    ____   __   __      __   __  ___
   / _  \ /  / /  /    /  /  \ \/  /
  / _/ / /  / /  /    /  /    \   /
 / _/ \ /  / /  /__  /  /__   /  /
/_____//__/ /______//______/ /__/

*/

Code package on esnips (thanks esnips!)

About these ads

One Comment

    • Anonymous
    • Posted January 7, 2014 at 2:47 am
    • Permalink

    Cool


One Trackback/Pingback

  1. By Direct3D starter « Bobobobo’s Weblog on 05 Jul 2009 at 5:46 pm

    [...] you have the DirectX sdk installed… many of the comments from the original were deleted in this [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 43 other followers

%d bloggers like this: