3D-часы на DirectX

Clock

Задача: создать 3D-часы.
Использованный API: DirectX 9.
Среда разработки: Visual Studio 2008.
Компилятор: VS08 C++.

Пример инициализации окна DirectX под Windows NT. Задача состоит в описании простой 3D-модели и успешном её рендеринге. В качестве модели решено было сделать часы, кроме того добавлен глобальный цикл с анимацией "ходьбы" часов. И в заключение -- переход в/из полноэкранный режим.

Примерная структура программы такова:

1. Инициализируем окно Windows. Настраиваем его для дальнейшего преобразования в DirectX-совместимое.
2. "Прикручиваем" к этому окну DirectX-окно. Задаём настройки рендеринга, различные графические эффекты, например качество мультисэмплинга. Большенство настроек зависят от видеокарты, на которой будет запускаться приложение. Поэтому эту часть можно доработать, добавив автоматическое определение поддерживаемых видеокартой технологий и применения их оптимальных настроек в программе.
3. Инициализация буфера вершин (их будет несколько)
4. "Рисование" в буфер вершин фигур (круг, окружность, отметки на циферблате, стрелки, ещё один круг "дальше от экрана" для создания 3D-эффекта.
5. Инициализация глобальной матрицы. Затем в главном цикле она будет плавно двигаться в разных наравлениях, периодически рандомно меняя своё направление движения. Таким образом будет достигнут эффект "плавания" часов в пространстве.
6. Главный цикл, в котором происходит изменение глобальной матрицы и обновление координат концов стрелок. Здесь же мы проводим рендеринг сцены.
7. Организуем переход в/из полноэкранный режим. С этим может возникнуть небольшая проблема, т.к. в полноэкранном режиме соотношение сторон экрана отличается от оконного (квадратного). И чтобы избежать нежелательного растягивания всей сцены в ширину, надо заново провести инициализацию "девайса" DirectX. Я пока не нашёл способа сделать это прямо в программе, поэтому схитрил -- при нажатии на кнопку перехода в полноэкранный режим, создаётся новое приложение, но теперь ему передаётся ещё и аргумент "-fullscreen", после этого текущее приложение сразу же завершается. Аргументы же обрабатываются в функции Main() - если их нет, то приложение запускается в оконном режиме, если же аргумент есть, то определяется текущее разрешение экрана, вычисляется коэффициент соотношения сторон, и согласно этим данным приложение запускается в полноэкранном режиме.

Для работы exe-шника вам понадобится DirectX 9, а так же видеокарта с поддержкой сглаживания 4х. Кроме того может понадобится Visual C++ 2008 Redistributable Package.
Для компиляции понадобится Visual Studio 2008 и DirectX SDK последней версии. Сглаживание 4х я ставил исходя из поддержки такового собственной видеокартой. То же относится и к аппаратной обработке вершин. Эти параметры вам возможно потребуется изменить для того, чтобы запустить исполняемый файл на вашем компьютере.

// Подключаем необходимые библиотеки
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "winmm.lib")
 
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <MMSystem.h>
HWND hInstance1;
LPDIRECT3D9 pDirect3D = NULL;				// интерфейс Direct3D
LPDIRECT3DDEVICE9 pDirect3DDevice = NULL;		// DirectX device
LPDIRECT3DVERTEXBUFFER9 pBufferVershin = NULL;		// буфер вершин для кольца
LPDIRECT3DVERTEXBUFFER9 pBufferVershinArrows = NULL;	// для стрелок
LPDIRECT3DVERTEXBUFFER9 pBufferVershin2 = NULL;		// для ещё одного кольца
 
bool FULLSCREEN = false;	// полноэкранный режим, получаем это значение из аргументов, передаваемых с ехе-шником
int WINDOW_WIDTH = 500;		// ширина окна
int WINDOW_HEIGHT = 500;	// высота окна
bool check3000 = true;		// контролирует был ли уже проведёт рандомный поворот матрицы
int randCheck = 0;		// вспомогательная переменная, random -- true или false. Зря она в глобальных объявлена.
 
// Вершина
struct CUSTOMVERTEX
{
	FLOAT X, Y, Z;
	DWORD color;
};
 
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)	// задаем тип вершины
#define CIRCLE_RES 60					// встроенной функции для рисования кольца нет
				// поэтому придётся рисовать его вручную. Эта константа задаёт "разрешение"
				// кольца -- из скольки секторов он будет состоять.
 
/*class MyArrow
{
public:
CUSTOMVERTEX points[6];
float x, y, fi, ro;
MyArrow(float xT, float yT, float roT);
void SetAngle(float fiT);
};
 
class MyClock
{
public:
float r;
MyArrow hour, minute, second;
};*/
 
// Крутим, вращаем наши часы
VOID Matrix()
{
	// Задаём матрица Мира, Вида и Проекции
	D3DXMATRIX MatrixWorld, MatrixWorldX, MatrixWorldY, MatrixWorld2;
	D3DXMATRIX MatrixView;
	D3DXMATRIX MatrixProjection;
 
	// MatrixWorld
	UINT Time = timeGetTime() % 24000;
	UINT Time2 = timeGetTime() % 60000; 
	FLOAT Angle = (sin((2.0f * D3DX_PI)*(Time2 / 60000.0f)))/4;  //(2.0f * D3DX_PI)*(Time / 25000.0f);
	FLOAT Angle1 = (cos((2.0f * D3DX_PI)*(Time2 / 60000.0f)))/4 + D3DX_PI; 
 
	float Angle2=0;
 
	if(check3000 && Time >= 15000)
	{
		randCheck = Time % 2;
		check3000 = false;
	} 
	else if(!check3000 && Time >= 3000 && Time<15000)
	{
		randCheck = Time % 2;
		check3000 = true;
	}
 
	// Если пришло время сменить направление движения матрицы, то меняем соответствующие углы
	if(randCheck)
		Angle2 = (sin((2.0f * D3DX_PI)*(Time / 24000.0f)))/4;
	else
		Angle2 = (cos((2.0f * D3DX_PI)*(Time / 24000.0f)))/4;
 
	D3DXMatrixRotationX(&MatrixWorldX, Angle);	// Поворачиваем матрицу по Х на угол
	D3DXMatrixRotationY(&MatrixWorldY, Angle1);	// по Y
	D3DXMatrixRotationY(&MatrixWorld2, Angle2);	
	D3DXMatrixMultiply(&MatrixWorld, &MatrixWorldX, &MatrixWorldY);
	D3DXMatrixMultiply(&MatrixWorld, &MatrixWorld, &MatrixWorld2);
	pDirect3DDevice->SetTransform(D3DTS_WORLD, &MatrixWorld);	// Трансформируем все вершины по глобальной матрице
 
	// MatrixView
	D3DXMatrixLookAtLH(&MatrixView, &D3DXVECTOR3(0.0f, 0.0f, -8.0f),
		&D3DXVECTOR3(0.0f, 0.0f, 0.0f),
		&D3DXVECTOR3(0.0f, 1.0f, 0.0f));
	pDirect3DDevice->SetTransform(D3DTS_VIEW, &MatrixView);
 
	// MatrixProjection
	int winH, winW;
	if(FULLSCREEN) 
	{
		winH = GetSystemMetrics(SM_CYSCREEN);
		winW = GetSystemMetrics(SM_CXSCREEN);
	}
	else
	{
		winH = WINDOW_HEIGHT;
		winW = WINDOW_WIDTH;
	}
	D3DXMatrixPerspectiveFovLH(&MatrixProjection, D3DX_PI/4,
		(float)winW/(float)winH, 1.0f, 100.0f);
	pDirect3DDevice->SetTransform(D3DTS_PROJECTION, &MatrixProjection);
}
 
// инициализируем буфер вершин. Фактически строим фигуры
HRESULT InitialBufferVershin()
{
	// Создаём массив для вершин круга
	CUSTOMVERTEX Vershin[CIRCLE_RES*4+2];
	const float dFi = (2*D3DX_PI)/CIRCLE_RES;	// размер сектора
	float Fi = 0;
	int i,j;
	for(i=0; i<CIRCLE_RES*2+2; i+=2)	// пишем вершины главной окружности и окружности более глубокой, кот. будет нарисована для 3D-эффекта
	{
		Vershin[i].color = 0x00ff0000;//0x00d2691e;
		Vershin[i+1].color = 0x00440000;
		Vershin[i].Z = -0.1f;
		Vershin[i+1].Z = -0.3f;
		Vershin[i].X = cos( Fi )*2.2f;
		Vershin[i+1].X = Vershin[i].X*1.05f;
		Vershin[i].Y = sin( Fi )*2.2f;
		Vershin[i+1].Y = Vershin[i].Y*1.05f;
		Fi += dFi;
	}
 
 
	// создаём штриховки на циферблате. Отталкиваемся от уже созданных вершин круга
 
	Vershin[CIRCLE_RES*2].X = Vershin[0].X;
	Vershin[CIRCLE_RES*2].Y = Vershin[0].Y;
	Vershin[CIRCLE_RES*2+1].X = Vershin[1].X;
	Vershin[CIRCLE_RES*2+1].Y = Vershin[1].Y;
 
	for(i=0, j=CIRCLE_RES*2+2; i<CIRCLE_RES*2; i+=2, j+=2)
	{
		Vershin[j].color = 0x00ff0000;//0x00d2691e;
		Vershin[j].Z = -0.095f;
		Vershin[j+1].color = 0x00d2691e;
		Vershin[j+1].Z = -0.095f;
		if( i % (CIRCLE_RES / 12) == 0) // часовое деление более длинное
		{
			Vershin[j].X = 0.8f * Vershin[i].X;
			Vershin[j].Y = 0.8f * Vershin[i].Y;
			Vershin[j+1].X = 0.9f * Vershin[i].X;
			Vershin[j+1].Y = 0.9f * Vershin[i].Y;
		}
		else	// обычные деления, короткие
		{
			Vershin[j].X = 0.86f * Vershin[i].X;
			Vershin[j].Y = 0.86f * Vershin[i].Y;
			Vershin[j+1].X = 0.89f * Vershin[i].X;
			Vershin[j+1].Y = 0.89f * Vershin[i].Y;
		}
 
	}
	// создаём буфер вершин из массива объектов
	if( FAILED( pDirect3DDevice -> CreateVertexBuffer(
		(CIRCLE_RES*4+2)*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX, // длина, использование, тип вершин
		D3DPOOL_DEFAULT, &pBufferVershin, NULL)))	// размешение в памяти, целевой объект буфер вершин
		return E_FAIL;
	VOID* pBV;	// нетиризированный указатель
	if( FAILED( pBufferVershin -> Lock(0, sizeof(Vershin), (void**)&pBV, 0)))	// блокируем буфер вершин
		return E_FAIL;
	memcpy(pBV, Vershin, sizeof(Vershin));	// копируем данные из массива в созданный выше буфер вершин
	pBufferVershin->Unlock();		// разблокируем буфер
 
	// Создаём буфер вершин для всего круга, затем мы его полностью зальём каким-то цветом и получим круг 
	// (выше создавали буфер вершин для окружности -- ободка часов).
	// Действия те же, что и выше.
	CUSTOMVERTEX Vershin2[CIRCLE_RES+2];
	Fi = 0;
	for(i=1; i<CIRCLE_RES+2; i++)
	{
		Vershin2[i].color = 0x00230000;
		Vershin2[i].Z = -0.1f;
		Vershin2[i].X = cos( Fi )*2.2f;
		Vershin2[i].Y = sin( Fi )*2.2f;
		Fi += dFi;
	}
	Vershin2[0].X = 0.0f;
	Vershin2[0].Y = 0.0f;
	Vershin2[0].color = 0x00230000;
	Vershin2[0].Z = -0.11f;
 
	Vershin2[CIRCLE_RES+1].X = Vershin2[1].X;
	Vershin2[CIRCLE_RES+1].Y = Vershin2[1].Y;
 
	if( FAILED( pDirect3DDevice -> CreateVertexBuffer(
		(CIRCLE_RES+2)*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
		D3DPOOL_DEFAULT, &pBufferVershin2, NULL)))
		return E_FAIL;
	VOID* pBV2;
	if( FAILED( pBufferVershin2 -> Lock(0, sizeof(Vershin2), (void**)&pBV2, 0)))
		return E_FAIL;
	memcpy(pBV2, Vershin2, sizeof(Vershin2));
	pBufferVershin2->Unlock();
 
	return S_OK;
}
 
// Производим перерасчёт и инициализируем буфер вершин для стрелок
HRESULT InitialBufferVershinArrows()
{
	if( pBufferVershinArrows != NULL)	// очищаем буфер
		pBufferVershinArrows -> Release();
 
	SYSTEMTIME* Time = new SYSTEMTIME;	// получаем системное время
 
	GetLocalTime(Time);			// переводим в локальное
 
	// Забиваем в массив координаты часовой, минутной и секундной стрелок
	CUSTOMVERTEX VershinArrows[6];
	VershinArrows[4].X = 1.3f * cos(D3DX_PI*2*(((Time->wHour%12)*60+Time->wMinute)/720.0f)+D3DX_PI/2);
	VershinArrows[4].Y = 1.3f * sin(D3DX_PI*2*(((Time->wHour%12)*60+Time->wMinute)/720.0f)+D3DX_PI/2);
	VershinArrows[4].color = 0x000f00ff;
	VershinArrows[4].Z = 0;
	VershinArrows[5].X = 0;
	VershinArrows[5].Y = 0;
	VershinArrows[5].color = 0x000f7fff;
	VershinArrows[5].Z = 0;
	VershinArrows[2].X = 1.8f * cos(D3DX_PI*2*((Time->wMinute*60+Time->wSecond)/3600.0f)+D3DX_PI/2);
	VershinArrows[2].Y = 1.8f * sin(D3DX_PI*2*((Time->wMinute*60+Time->wSecond)/3600.0f)+D3DX_PI/2);
	VershinArrows[2].color = 0x000f00ff;
	VershinArrows[2].Z = 0.01f;
	VershinArrows[3].X = 0;
	VershinArrows[3].Y = 0;
	VershinArrows[3].color = 0x000f7fff;
	VershinArrows[3].Z = 0.01f;
	VershinArrows[0].X = 1.8f * cos(D3DX_PI*2*(Time->wSecond/60.0f)+D3DX_PI/2);
	VershinArrows[0].Y = 1.8f * sin(D3DX_PI*2*(Time->wSecond/60.0f)+D3DX_PI/2);
	VershinArrows[0].color = 0x008b3a3a;
	VershinArrows[0].Z = 0.02f;
	VershinArrows[1].X = -VershinArrows[0].X * 0.2f;
	VershinArrows[1].Y = -VershinArrows[0].Y * 0.2f;
	VershinArrows[1].color = 0x00cd5555;
	VershinArrows[1].Z = 0.02f;
 
 
 
	// создаём буфер вершин, хранящий стрелки
	if( FAILED( pDirect3DDevice -> CreateVertexBuffer(
		6*sizeof(CUSTOMVERTEX), 0, D3DFVF_CUSTOMVERTEX,
		D3DPOOL_DEFAULT, &pBufferVershinArrows, NULL)))
		return E_FAIL;
	VOID* pBV;
	if( FAILED( pBufferVershinArrows -> Lock(0, sizeof(VershinArrows), (void**)&pBV, 0)))
		return E_FAIL;
	memcpy(pBV, VershinArrows, sizeof(VershinArrows));
	pBufferVershinArrows->Unlock();
	return S_OK;
}
 
// Инициализируем Direct3D
HRESULT InitialDirect3D(HWND hwnd)
{
	if( NULL == (pDirect3D = Direct3DCreate9(D3D_SDK_VERSION))) // создаём указатель на интерфейс
		return E_FAIL;
	D3DDISPLAYMODE Display;		// будет использовано при создании заднего буфера
	if( FAILED( pDirect3D -> GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &Display)))
		return E_FAIL;
	D3DPRESENT_PARAMETERS Direct3DParametr;	// параметры, кот. будут использованы при создании DirectX девайса (окна Windows с поддержкой DirectX)
	ZeroMemory(&Direct3DParametr, sizeof(Direct3DParametr));
	if(FULLSCREEN)	// если приложение запущено в полноэкранном режиме, делаем соответствующие установки
	{
		Direct3DParametr.BackBufferHeight = Display.Height;
		Direct3DParametr.BackBufferWidth = Display.Width;
		Direct3DParametr.BackBufferCount = 3;
		Direct3DParametr.FullScreen_RefreshRateInHz = Display.RefreshRate;
	}
	Direct3DParametr.EnableAutoDepthStencil = TRUE;
	Direct3DParametr.AutoDepthStencilFormat = D3DFMT_D16;
	Direct3DParametr.Windowed = !FULLSCREEN;	// оконный или полноэкранный режим
	Direct3DParametr.SwapEffect = D3DSWAPEFFECT_DISCARD;	// задний и текущий буферы будут меняться только если рендеринг в задний буфер был полностью завершён 
	Direct3DParametr.BackBufferFormat = Display.Format;	// формат заднего буфера
	Direct3DParametr.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES; // мультисэмплинг, измените под свою видеокарту
	if( FAILED( pDirect3D -> 
		CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, // видеокарта, аппаратное ускорение, дескриптор главного окна
		D3DCREATE_HARDWARE_VERTEXPROCESSING, &Direct3DParametr, &pDirect3DDevice))) // обработка вершин, параметры выше, целевой указатель интерфейса
		return E_FAIL;
	// указываем различные параметры рендеринга, такие как отсутствие Z-буфера, свет, мультисэмплинг и антиальясинг
	pDirect3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
	pDirect3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
	pDirect3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
	pDirect3DDevice->SetRenderState(D3DRS_MULTISAMPLEANTIALIAS, TRUE);
	pDirect3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, TRUE);
	return S_OK;
}
 
 
VOID RenderingDirect3D() 
{
	if( pDirect3DDevice == NULL) // если интерфейс создан, то можно приступать к рендерингу
		return;
	// чистим экран (точнее задний буфер)
	pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(39, 64, 139), 1.0f, 0); //(23, 95, 54)
 
	// Непосредственно само рисование и создание сцены проходит между pDirect3DDevice->BeginScene() и pDirect3DDevice->EndScene()
 
	pDirect3DDevice->BeginScene();
 
	// Здесь рисуем
	Matrix();	// поворачиваем глобальную матрицу
 
	// Рисуем цилиндр по краям часов, уходящий вглубь экрана, который придаст объём часам
	// указываем источник для рисования фигуры
	pDirect3DDevice->SetStreamSource(0, pBufferVershin, 0, sizeof(CUSTOMVERTEX));
	// указываем тип вершин
	pDirect3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
	// рисуем "примитив", в данном случае треугольник, начиная с 0-й вершины, рисуем CIRCLE_RES*2 фигур (треугольников)
	pDirect3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, CIRCLE_RES*2);
 
	// Рисуем сам циферблат, круг
	pDirect3DDevice->SetStreamSource(0, pBufferVershin2, 0, sizeof(CUSTOMVERTEX));
	pDirect3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
	pDirect3DDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, CIRCLE_RES);
 
	// Рисуем минутные штриховки на циферблате
	pDirect3DDevice->SetStreamSource(0, pBufferVershin, 0, sizeof(CUSTOMVERTEX));
	pDirect3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
	pDirect3DDevice->DrawPrimitive(D3DPT_LINELIST, CIRCLE_RES*2+2, CIRCLE_RES);
 
	// рисуем стрелки
	pDirect3DDevice->SetStreamSource(0, pBufferVershinArrows, 0, sizeof(CUSTOMVERTEX));
	pDirect3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
	pDirect3DDevice->DrawPrimitive(D3DPT_LINELIST, 0, 3);
	// - - - - - - -
 
	pDirect3DDevice->EndScene();
	pDirect3DDevice->Present(NULL, NULL, NULL, NULL);	// Выводим задний буфер на экран
}
 
// освобождение ресурсов
VOID DeleteDirect3D()
{
	if( pBufferVershinArrows != NULL)
		pBufferVershinArrows -> Release();
	if( pBufferVershin != NULL)
		pBufferVershin -> Release();
	if( pDirect3DDevice != NULL)
		pDirect3DDevice -> Release();
	if( pDirect3D != NULL)
		pDirect3D -> Release();
}
 
LRESULT CALLBACK MainWinProc(HWND hwnd,		// дескриптор окна
							 UINT msg,	// идентификатор сообщения
							 WPARAM wparam,	// дополнительная информация
							 LPARAM lparam) // дополнительная информация
{
	switch(msg) 
	{
		/*case WM_PAINT:
		{
		RenderingDirect3D();
		ValidateRect(hwnd, NULL);
		}	break; */
	case WM_DESTROY:	// если получили сообщение о выходе, то уничтожаем ресурсы директ икс и выходим
		{
			DeleteDirect3D();
			PostQuitMessage(0);
			return(0);
		}	break;
 
	case WM_KEYDOWN:
		{
			if(wparam == VK_RETURN)	// при нажатии на "Enter"
			{
				// получаем текущий список аргументов. В принципе эти 3 строки можно убрать.
				LPWSTR *szArglist;
				int nArgs;
				szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
				// если сейчас приложение запущено в оконом режиме, запускаем его в полноэкранном
				if(!FULLSCREEN)
					ShellExecute(hInstance1,L"open",szArglist[0], L"-f", NULL, SW_SHOWNORMAL);
				else	// и наоборот
					ShellExecute(hInstance1,L"open",szArglist[0], NULL, NULL, SW_SHOWNORMAL);
				// в эту милисекунду у нас работает 2 программы.
				PostQuitMessage(0); // но на практике это не заметно вообще, так как мы сразу
						// после запуска новой копии, выходим из старой.
 
			} else
			if(wparam == VK_ESCAPE)	// если нажат эскейп, выходим
				PostQuitMessage(0);
			return(0);
		}	break;
	}
	return (DefWindowProc(hwnd, msg, wparam, lparam));
}
 
int WINAPI WinMain(HINSTANCE hinstance, 
				   HINSTANCE hPrevInstance, 
				   LPSTR lpCmdLine, 
				   int nShowCmd) 
{
	// получаем список переданных с командной строкой аргументов
	// если есть аргумент "-f", то запускаем в полноэкранном режиме
	LPWSTR *szArglist1;
	int nArgs;
	LPWSTR temp = L"-f";
	szArglist1 = CommandLineToArgvW(GetCommandLineW(), &nArgs);
	if(szArglist1 != NULL && nArgs==2) 
			FULLSCREEN = true;
	WNDCLASSEX windowsclass;	// создаём класс
	HWND hwnd;			// создаём дескриптор окна
	MSG msg;			// идентификатор сообщения
 
	// определим класс окна WNDCLASSEX
	windowsclass.cbSize = sizeof(WNDCLASSEX);
	windowsclass.style = CS_DBLCLKS|CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
	windowsclass.lpfnWndProc = MainWinProc;
	windowsclass.cbClsExtra = 0;
	windowsclass.cbWndExtra = 0;
	windowsclass.hInstance = hinstance;
	windowsclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	windowsclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	windowsclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
	windowsclass.lpszMenuName = NULL;
	windowsclass.lpszClassName = L"WINDOWSCLASS";
	windowsclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
	// зарегистрируем класс
	if(!RegisterClassEx(&windowsclass))
		return 0;
	// можно создать окно
	if(!(hwnd = CreateWindowEx(NULL, L"WINDOWSCLASS", L"Часы на DirectX9 by RazeR", // стиль окна, класс, название окна
		(WS_OVERLAPPEDWINDOW|WS_VISIBLE)-(/*WS_MINIMIZEBOX | */WS_MAXIMIZEBOX | WS_THICKFRAME), 
		(GetSystemMetrics(SM_CXSCREEN)-WINDOW_WIDTH)/2,		// левый верхний 
		(GetSystemMetrics(SM_CYSCREEN)-WINDOW_HEIGHT)/2,	//		угол
		WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hinstance, NULL)))	// ширина, высота, дескриптор родительского окна, дескриптор меню, дескриптор приложения, указатель на данные окна
		return 0;
	if(FULLSCREEN) ShowCursor(false);	// прячем курсор
	if( SUCCEEDED( InitialDirect3D(hwnd)))	// инициализируем Директ 3Д 
	{
		if( SUCCEEDED( InitialBufferVershin()))	// инициализируем буфер вершин
		{
			ShowWindow(hwnd, SW_SHOWDEFAULT);	// рисуем окно
			UpdateWindow(hwnd);			// обновим окно
			ZeroMemory(&msg, sizeof(msg));
			while( msg.message != WM_QUIT)	// главный цикл, здесь читаем входящие сообщения от пользователя
			{
				if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
				else
					if( SUCCEEDED( InitialBufferVershinArrows()))
						RenderingDirect3D();
			}
		}
	}
 
 
	return 0;
}
/*
MyArrow::MyArrow(float xT, float yT, float roT) 
{
for(int i=0; i<6; i++) 
{
points[i].X = 0;
points[i].Y = 0;
points[i].Z = 0.5;
points[i].rhw = 1.0;
points[i].color = 0x00000fff;
}
x = xT;
y = yT;
ro = roT;
fi = 0;
}*/

ВложениеРазмер
Clock.rar350.29 кб