Now that you've learned the basics of 3D primitives and the render states you can use to affect how the primitives are rendered, let's look at a program that actually renders something interesting! The RoadRage code for this chapter is a lot bigger than in previous chapters, but the steps that it is taking should make sense to you. Aside from all the initialization and Windows code that we've talked about in previous chapters, here's how RoadRage works.
For simple programs, you can specify the 3D models in the code itself. But for a significant 3D program, you'll probably want to load 3D model information from external files. In RoadRage, the function CMyD3DApplication::LoadRR_Resources loads all the media that the program will use. LoadRR_Resources and CMyD3DApplication::InitRRVariables, which sets up some application state, are called by WinMain before calling CMyD3DApplication::Run to start the main message loop. You could also load the media in the CMyD3DApplication::OneTimeSceneInit function.
Every time the Direct3D device is created or changed, you'll want to initialize some settings. RoadRage does this in the CMyD3DApplication::InitDeviceObjects function shown here:
//------------------------------------------------------------------- // Name: InitDeviceObjects // Desc: Initializes scene objects //------------------------------------------------------------------- HRESULT CMyD3DApplication::InitDeviceObjects() { // Create and set up the object material. D3DMATERIAL7 mtrl; D3DUtil_InitMaterial( mtrl, 1.0f, 1.0f, 1.0f, 0.0f ); mtrl.power = 40.0f; m_pd3dDevice->SetMaterial( &mtrl ); m_pd3dDevice->SetRenderState( D3DRENDERSTATE_AMBIENT, 0x00505050 ); // Set the transform matrices. D3DUtil_SetIdentityMatrix( matWorld ); D3DUtil_SetProjectionMatrix( matProj, 1.57f, 1.0f, 1.0f, 100.0f ); m_pd3dDevice->SetTransform( D3DTRANSFORMSTATE_WORLD, &matWorld ); m_pd3dDevice->SetTransform( D3DTRANSFORMSTATE_PROJECTION, &matProj ); // Turn on lighting. Light will be set during the FrameMove() // call. m_pd3dDevice->SetRenderState( D3DRENDERSTATE_LIGHTING, bEnableLighting ); // Set miscellaneous render states. m_pd3dDevice->SetRenderState(D3DRENDERSTATE_DITHERENABLE, TRUE); m_pd3dDevice->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE); m_pd3dDevice->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_TRUE); m_pd3dDevice->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID); m_pd3dDevice->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD); return S_OK; } |
You can see that it sets the material on the device to be a normal white material. Then it sets the ambient light level. Then it sets up the world and projection matrices. (The view matrix will be set later.) Finally, it sets some render states to affect how the graphics will be rendered.
Before rendering each frame, the program needs to react to user input, update object positions, and so on. In RoadRage, this is all handled in the CMyD3DApplication::FrameMove function. We haven't added input support to RoadRage, so the function doesn't do much yet. But you can see that at the end of the function, it adjusts the view matrix based on the user's position and orientation.
After updating the world state, it's time to render the scene. This task is done by CMyD3DApplication::Render. RoadRage uses a culling system that renders a subset of the world depending on the camera's position and orientation. Management of that culling system takes up a lot of the code in this function, but the core tasks in Render are pretty simple: