#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9math.h>
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
static LRESULT CALLBACK MainWindowProc(HWND,UINT,WPARAM,LPARAM);
static TCHAR szMain[] = _T("Main");
static const int SIDE_NUM = 10; // 底面多边形边数
static const float SIDE_LEN_1 = 10; // 底面边长
static const float SIDE_LEN_2 = 12; // 腰部多边形边长
static const float HIGH_1 = 5; // 底面到腰的高度
static const float HIGH_2 = 40; // 底面到尖端的高度
static LPDIRECT3D9 g_pD3D;
static LPDIRECT3DDEVICE9 g_pDevice;
static LPDIRECT3DVERTEXBUFFER9 g_pVB; // 所有顶点
static LPDIRECT3DINDEXBUFFER9 g_pIndexBuffer1;// 底面
static LPDIRECT3DINDEXBUFFER9 g_pIndexBuffer2;// 上半截的外表面
static LPDIRECT3DINDEXBUFFER9 g_pIndexBuffer3;// 下半截的外表面
static LPDIRECT3DTEXTURE9 g_pTexture;
static int g_nRotate;
static BOOL InitD3D(HWND);
static BOOL InitGeometry();
static void SetupMatrices();
static void SetLight();
static void SetMaterial1();
static void SetMaterial2();
static void Render();
static void Cleanup();
struct CUSTOMVERTEX
{
D3DXVECTOR3 position;
D3DXVECTOR3 normal;
float tu,tv;// 纹理坐标
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpCmdLine,int nShowCmd)
{
MSG msg;
HWND hMainWnd;
DWORD style;
long x,y,w,h;
WNDCLASS wndclass;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hIcon = LoadIcon(NULL,MAKEINTRESOURCE(IDI_APPLICATION));
wndclass.hInstance = hInst;
wndclass.lpszMenuName = NULL;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.cbWndExtra = 0;
wndclass.hbrBackground = CreateSolidBrush(RGB(0,0,255));
wndclass.hCursor = LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW));
wndclass.lpfnWndProc = MainWindowProc;
wndclass.lpszClassName = szMain;
RegisterClass(&wndclass);
style = WS_OVERLAPPEDWINDOW;// WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN;
w = 400; h = 300;
x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
hMainWnd = CreateWindow(szMain,_T("Direct3D 初级教程"),style,
x,y,w,h,NULL,NULL,hInst,NULL);
ShowWindow(hMainWnd,SW_SHOW);
do
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (WM_QUIT == msg.message) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Render();
g_nRotate += 1;
g_nRotate %= 360;
}
} while(1);
return msg.wParam;
}
static LRESULT CALLBACK MainWindowProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
if (!InitD3D(hwnd) || !InitGeometry())
{
MessageBox(hwnd,_T("初始化Direct3D失败"),_T("错误"),MB_OK | MB_ICONERROR);
return -1;
}
g_nRotate = 0;
return 0;
case WM_PAINT:
Render();
break;
case WM_DESTROY:
Cleanup();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
static BOOL InitD3D(HWND hwnd)
{
D3DPRESENT_PARAMETERS d3dpp;
HRESULT hr;
g_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (NULL == g_pD3D) return FALSE;
ZeroMemory(&d3dpp,sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
// 创建16位Z缓存
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
hr = g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, // 使用默认显卡
D3DDEVTYPE_HAL, // 设备类型,HAL,硬件抽象层,无需显卡支持
hwnd, // 协作窗口
D3DCREATE_SOFTWARE_VERTEXPROCESSING,// 软件顶点处理
&d3dpp,&g_pDevice);
if (D3D_OK != hr) return FALSE;
/* 启用Z缓存,允许消隐处理 */
g_pDevice->SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE);
// 打开光照处理
g_pDevice->SetRenderState(D3DRS_LIGHTING,TRUE);
// 自动对法线矢量进行单位化处理
g_pDevice->SetRenderState(D3DRS_NORMALIZENORMALS,TRUE);
g_pDevice->SetRenderState(D3DRS_SHADEMODE,D3DSHADE_GOURAUD);
return TRUE;
}
static BOOL InitGeometry()
{
CUSTOMVERTEX vertices[2 * SIDE_NUM + 2];
void* pVertices;
int i,count;
float theta,radius;
HRESULT hr;
/* 从图片文件创建纹理对象 */
hr = D3DXCreateTextureFromFile(g_pDevice,_T("texture.jpg"),&g_pTexture);
/* 宝石底面 */
vertices[0].position = D3DXVECTOR3(0.0f,0.0f,0.0f);//底面中心点的坐标
vertices[0].normal = D3DXVECTOR3(0.0f,0.0f,1.0f); //底面中心点的法向量
vertices[0].tu = 0.5f;
vertices[0].tv = 0.5f;
radius = (float)((SIDE_LEN_1 / 2.0) / sin(D3DX_PI / SIDE_NUM));
for(i = 0; i < SIDE_NUM; i++)
{
theta = i * 2 * D3DX_PI / SIDE_NUM;
vertices[i + 1].position = D3DXVECTOR3((float)(radius * cos(theta)),(float)(radius * sin(theta)),0.0f);
vertices[i + 1].normal = D3DXVECTOR3((float)cos(theta),(float)sin(theta),0.0f);
vertices[i + 1].normal = D3DXVECTOR3(0.0f,0.0f,-1.0f);
// 计算纹理坐标
vertices[i + 1].tu = (float)(0.5 * (1 + cos(theta)));
vertices[i + 1].tv = (float)(0.5 * (1 + sin(theta)));
}
/* 宝石腰部的各顶点 */
count = (SIDE_NUM + 1);
radius = (float)((SIDE_LEN_2 / 2.0) / sin(D3DX_PI / SIDE_NUM));
for(i = 0; i < SIDE_NUM; i++)
{
theta = i * 2 * D3DX_PI / SIDE_NUM;
vertices[i + count].position = D3DXVECTOR3((float)(radius * cos(theta)),(float)(radius * sin(theta)),HIGH_1);
vertices[i + count].normal = D3DXVECTOR3((float)cos(theta),(float)sin(theta),0.0f);
// 计算纹理坐标
vertices[i + count].tu = (float)(0.5 * (1 + cos(theta)));
vertices[i + count].tv = (float)(0.5 * (1 + sin(theta)));
}
count += SIDE_NUM;
vertices[count].position = D3DXVECTOR3(0.0f,0.0f,HIGH_2);
vertices[count].normal = D3DXVECTOR3(0.0,0.0f,1.0f);
vertices[count].tu = 0.0f;
vertices[count].tv = 0.5f;
/* 创建顶点缓冲区,复制顶点数据到其中 */
hr = g_pDevice->CreateVertexBuffer(sizeof(vertices),0,
D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&g_pVB,NULL);
if (D3D_OK != hr) return FALSE;
g_pVB->Lock(0,sizeof(vertices),(void**)&pVertices,0);
CopyMemory(pVertices,vertices,sizeof(vertices));
g_pVB->Unlock();
/*
创建底面的索引缓冲区,底面有SIDE_NUM个三角形,当作三角形扇形处理
注意点的排列次序和平面正面以及法线方向的问题
*/
WORD index_1[SIDE_NUM + 2];
index_1[0] = 0;
for(i = 0; i < SIDE_NUM; i++)
index_1[i+1] = (WORD)(SIDE_NUM - i);
index_1[SIDE_NUM + 1] = SIDE_NUM;
hr = g_pDevice->CreateIndexBuffer(sizeof(index_1),
0,D3DFMT_INDEX16,
D3DPOOL_DEFAULT,&g_pIndexBuffer1,NULL);
if (D3D_OK != hr) return FALSE;
void* pIndices;
g_pIndexBuffer1->Lock(0,sizeof(index_1),(void**)&pIndices,0);
CopyMemory(pIndices,index_1,sizeof(index_1));
g_pIndexBuffer1->Unlock();
/*
创建底面和腰之间外侧面的索引缓冲区
SIDE_NUM个梯形侧面,每个梯形侧面由2个三角形组成,
将整个侧面看作是由2 * SIDE_NUM个顶点,含有2 * SIDE_NUM个三角形的三角形带组成的
注意点的排列次序和平面正面以及法线方向的问题
*/
WORD index_2[2 * SIDE_NUM + 2];
for(i = 0; i < SIDE_NUM; i++)
{
index_2[2 * i + 0] = (WORD)(i + SIDE_NUM + 1);
index_2[2 * i + 1] = (WORD)(i + 1);
}
index_2[2 * SIDE_NUM] = SIDE_NUM + 1;
index_2[2 * SIDE_NUM + 1] = 1;
hr = g_pDevice->CreateIndexBuffer(sizeof(index_2),
0,D3DFMT_INDEX16,
D3DPOOL_DEFAULT,&g_pIndexBuffer2,NULL);
if (D3D_OK != hr) return FALSE;
g_pIndexBuffer2->Lock(0,sizeof(index_2),(void**)&pIndices,0);
CopyMemory(pIndices,index_2,sizeof(index_2));
g_pIndexBuffer2->Unlock();
/*
创建腰和顶点之间的外侧面的索引缓冲区
有SIDE_NUM个三角形的侧面,看作是含有SIDE_NUM个三角形的三角形扇形
注意点的排列次序和平面正面以及法线方向的问题
*/
WORD index_3[SIDE_NUM + 2];
index_3[0] = 2 * SIDE_NUM + 1;
for(i = 0; i < SIDE_NUM; i++)
index_3[i+1] = (WORD)(i + SIDE_NUM + 1);
index_3[SIDE_NUM + 1] = SIDE_NUM + 1;
hr = g_pDevice->CreateIndexBuffer(sizeof(index_3),
0,D3DFMT_INDEX16,
D3DPOOL_DEFAULT,&g_pIn