SharewareСтатьи

Загрузка 3D моделей

Автор:

Загрузка 3D моделей


Для разработчика игр важнейшими элементами, с которыми он постоянно сталкивается и работает, являются 3D-модели. Ни одна современная игра не обходится без 3Д-моделей. Причем качество этих моделей находится на высочайшем уровне, и с каждой выпущенной игрой этот уровень повышается. Нам с вами, как начинающим разработчикам игр, такое качество светить будет еще очень не скоро))). Но начинать обучаться использовать 3Д-модели должен каждый, кто хочет в будущем написать собственную игру.
Большинство дает уроки по освещению, материалам, вершинам и т.д. и т. п., но я считаю, что рисовать кубики-рубики и включать свет может научится каждый. Причем сам. Например книга «Введение в программирование трехмерных игр с DirectX9.0», прекрасно описывает все основы д3д. Всё же в следующих статьях я планирую затронуть и эти темы. А сейчас рассмотрим простое приложение, которое загружает и рисует 3Д-модель из .Х-файла. .Х-файл можно создать с помощью 3DSMAX и конвертера в формат .Х. То есть создаем модель в 3DMAX`е (надеюсь, вы умеете создавать хоть какие-то модели), жмем File->Export, выбираем из Тип файла-> X-File (Q3D) (*.X), вводим имя файла, и жмём Сохранить. В окне, которое появится нажимаем Go!. Теперь файл сохранен в формате, который является родным для Direct3D. Конвертер в формат .Х можно скачать из раздела Program Files, там есть и инструкция по использованию. Далее привожу полный код программки, что использует нашу модель, и выводит ее на экран. Для работы программы нужен DoomCross®3D.h, который тоже находится в разделе Program Files.


//***************************************************************************
#include

ID3DXMesh* Mesh = 0;
D3DMATERIAL9 Mtrls[1000000];
IDirect3DTexture9* Textures[1000000];
D3DLIGHT9 LIGHT,LIGHTP;
DWORD n=1;
//***************************************************************************
void OPTIONS ()
{
GAMENAME="A simple 3D initialization";
}
//***************************************************************************
void LOADING ()
{
D3DXMatrixPerspectiveFovLH(&PROJ, D3DX_PI * 0.6f, 1.3f, 1.0f, 10000.0f);
DEVICE->SetTransform(D3DTS_PROJECTION, &PROJ);

HRESULT hr = 0;
ID3DXBuffer* adjBuffer = 0;
ID3DXBuffer* mtrlBuffer = 0;
DWORD numMtrls = 0;

hr = D3DXLoadMeshFromX("C:\\FILENAME.x",
                        D3DXMESH_MANAGED,
                        DEVICE,
                        &adjBuffer,
                        &mtrlBuffer,
                        0,
                        &numMtrls,
                        &Mesh);

if(FAILED(hr))
{
MessageBox(0, "D3DXLoadMeshFromX() - FAILED", 0, 0);
}

adjBuffer->Release();
mtrlBuffer->Release();

if(mtrlBuffer != 0 && numMtrls != 0)
{
D3DXMATERIAL* mtrls=(D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();

for(DWORD i = 0; i < numMtrls; i++)
{
mtrls.MatD3D.Ambient = mtrls.MatD3D.Diffuse;
Mtrls
=(mtrls.MatD3D);

if( mtrls
.pTextureFilename != 0 )
{
IDirect3DTexture9* tex = 0;
D3DXCreateTextureFromFile(DEVICE,mtrls
.pTextureFilename,&tex);
Textures
=(tex);
n++;
}

else
{
Textures
=(0);
}

}

}

ID3DXMesh* pTempMesh = 0;
Mesh->CloneMeshFVF(D3DXMESH_MANAGED,
                  Mesh->GetFVF() |
                  D3DFVF_NORMAL,
                  DEVICE,
                  &pTempMesh);

D3DXComputeNormals(pTempMesh, 0);
Mesh->Release();
Mesh = pTempMesh;

DEVICE->SetRenderState(D3DRS_LIGHTING, true);
::ZeroMemory(&LIGHT, sizeof(LIGHT));
LIGHT.Type = D3DLIGHT_DIRECTIONAL;
LIGHT.Ambient = D3DXCOLOR(0.3f, 0.3f, 0.3f, 1.0f);
LIGHT.Diffuse = D3DXCOLOR(0.6f, 0.6f, 0.6f, 1.0f);
LIGHT.Specular = D3DXCOLOR(0.1f, 0.1f, 0.1f, 1.0f);
LIGHT.Direction=D3DXVECTOR3(0.0f, 0.0f, 1.0f);
DEVICE->SetLight(0, &LIGHT);
DEVICE->LightEnable(0, true);
DEVICE->SetRenderState(D3DRS_NORMALIZENORMALS, true);
DEVICE->SetRenderState(D3DRS_SPECULARENABLE, true);
}
//***************************************************************************
void RENDER ()
{
DEVICE->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0 );
DEVICE->BeginScene();
D3DXMatrixTranslation(&WORLD,0,-4,100);
DEVICE->SetTransform(D3DTS_WORLD, &WORLD);

for(int i = 0; i < n+1; i++)
{
DEVICE->SetMaterial(&Mtrls);
DEVICE->SetTexture(0, Textures
);
Mesh->DrawSubset(i);
}

DEVICE->EndScene();

DEVICE->Present(0, 0, 0, 0);

if (KEYS[VK_ESCAPE]) {SendMessage(hWnd,WM_CLOSE,0,0);}
}
//***************************************************************************


В результате у меня получилось нечто похожее, как на картинке. Не знаю, какую модель используете вы, но работать должно обязательно, так как код скопирован прямо из Visual C++ (шестой). Не забудьте вставить в папку с программкой файл d3dx9_32.dll, так как без него ничего не получится.
Теперь рассмотрим код поподробнее.
ID3DXMesh* Mesh. В структуре ID3DXMesh хранятся все данные о нашем объекте. То есть координаты всех точек данного объекта. Здесь также хранится буфер индексов, который сохраняет данные о том, как точки группируются в вершины. Весь ID3DXMesh (или сетка) состоит из нескольких подгрупп, которые обладают похожими свойствами, а каждая подгруппа, в свою очередь, состоит из одного, нескольких или из невероятного неисчислимо большого))) количества треугольников. Чтобы нарисовать объект, мы не используем что-то типа Объект-> Нарисуйся(), вместо этого мы рисуем все подгруппы треугольников с помощью цикла.
OPTIONS () Задает название приложения.
LOADING () Основная процедура в этой программке. В ней сначала стандартно задаются матрицы проекций. Далее мы используем D3DXLoadMeshFromX для загрузки нашей модели из файла FILENAME.x, то есть в этом параметре задается путь к нашему объекту, который мы создали в 3DSMAX. В следующем цикле мы сохраняем данные о текстурах и материалах нашего объекта в Mtrls и Textures. Потом вычисляем нормали. Для этого создаем еще один ID3DXMesh и используем D3DXComputeNormals. После этого присваиваем нашему старому Mesh новый pTempMesh. В конце задаем освещение, так как без света ничего не будет видно (сколько времени у меня ушло, чтобы это понять))).
RENDER () Здесь мы выводим наш объект на экран. Сначала смещаем его немного назад и вниз с помощью D3DXMatrixTranslation, DEVICE->SetTransform. Потом с помощью цикла рисуем каждую подгруппу нашего Mesh, с наложенными на нее материалом и текстурой.
Более подробно все функции, связанные с сетками, рассматриваются в Части III, главах 10-11 книги «Введение в программирование трехмерных игр с DirectX9.0». Я прежде всего ставил перед собой задачу показать сам алгоритм загрузки и рисования 3Д-объекта, а не подробное рассмотрение работы всех функций. Всё это прекрасно описывается в книге.
В ходе испытаний программки я наткнулся на одну проблему. Когда в 3DMAX`е создаем какой-то объект, накладаем на него текстуру, а потом конвертируем в .Х, то в .Х-файл записываются те пути к текстурам, где они находятся на этом компьютере. То есть, если просто поместить текстуры в папку с программкой, то на другом ПК она работать уже не будет. Я так и не нашел решение этой проблемы.

13 марта 2009