Экспорт из Blender в разработке игр.
главная страница статьи файлы о сайте ссылки
Экспорт из Blender в разработке игр
Загрузка и отображение в Delphi

Георгий Мошкин
tmtlib@narod.ru

Оглавление.

файлы по теме:

blender2006.zip (546 kb) - 3d модель, исходники экспортера для Blender и загрузчика на Delphi + exe
blender2006noexe.zip (351 kb) - всё то же, но без exe

Займёмся загрузкой нашей модели в Delphi. Для начала нужно раздобыть какой-нибудь framework для работы с OpenGL. Для этой цели отлично подойдёт заготовка от NeHe (http://nehe.gamedev.net). Скачать вы её можете прямо с этой страницы - zagotovka.zip (12kb). Так как мы собираемся загрузить оттекстурированную модель, то сразу раздобудем какой-нибудь простенький модуль по загрузке BMP файлов. Для наших целей отлично подойдёт модуль bmp.pas с сайта http://www.sulaco.co.za - это маленький файл bmppas.zip (1kb). Ну вот, мы хорошенько подготовились к загрузке наших моделей. Рассмотрим кусочек экспортированной в наш формат модели:

Мы записали модель в текстовый файл. Нам предстоит считать эти данные и преобразовать их из текста в числа. Дело в том, что readln(f,s) даст нам переменную типа string. В некоторых строках у нас содержится по несколько чисел - координаты вершин x,y,z, нормали, а также текстурные координаты u,v. Из этих строк нужно будет вытащить по три маленьких переменных типа string, и уже только потом преобразовывать их в single. Для вытаскивания текста из больших строк очень удобно использовать модуль _strman.pas - strman.zip (7kb).

1. Как выглядит модель в нашем формате? Она состоит из кучи полигонов, описание каждого из которых начинается со слова face:

face - заголовок полигона
0.bmp - имя файла с текстурой для полигона
4 - количество вершин в полигоне
1.000000 1.000000 -1.000000 - x,y,z координаты первой вершины
1.000000 -1.000000 -1.000000 - x,y,z координаты второй вершины
-1.000000 -1.000000 -1.000000 - x,y,z координаты третьей вершины
-1.000000 1.000000 -1.000000 - x,y,z координаты четвёртой вершины
0.577349 0.577349 -0.577349 - нормаль, торчащая из первой вершины
0.577349 -0.577349 -0.577349 - нормаль, торчащая из второй вершины
-0.577349 -0.577349 -0.577349 - нормаль, торчащая из третьей вершины
-0.577349 0.577349 -0.577349 - нормаль, торчащая из четвёртой вершины
0.000000 1.000000 - текстурные координаты в первой вершине
0.000000 0.000000 - текстурные координаты во второй вершине
1.000000 0.000000 - текстурные координаты в третьей вершине
1.000000 1.000000 - текстурные координаты в четвёртой вершине

 

2. Запустим Delphi и откроем пример по работе с OpenGL от NeHe. После того, как мы откроем dpr-овский файл, нужно будет ещё через file > Open... открыть юнит nehegl.pas. Порывшись внутри юнита nehegl.pas следует отыскать процедуру вывода на экран - это процедура Draw:

Вот сюда-то мы и будем писать нашу программу, удалив исходный код вывода ненужной крутящейся штуки на экран.

3. Начнём с описания типа для хранения загруженной модели в памяти. Так как модель состоит из полигонов, то нужно описать тип TPoligon:

type TVector=record
              x,y,z : single;
             end;

type TUVCoord=record
               u,v : single;
              end;

type TPoligon=record
               BMPname   : string;
               vertnum   : integer;
               vert      : array[0..3] of TVector;
               norm      : array[0..3] of TVector;
               texcoord  : TUVCoord;


               gltex     : GLUINT;
              end;

Как видно - это в полности соответствует нашему текстовому файлу - сначала имя текстуры, потом количество вершин vertnum, потом координаты вершин и нормалей, текстурные координаты. Но в самом конце я добавил ещё одну переменную gltex - в неё мы будем записывать идентификатор OpenGL-евской текстуры (этот идентификатор выдаст функция LoadTexture из файла bmp.pas).

 

4. Добавим переменную для хранения полигонов:

var Poligoni:array of TPoligon;

 

5.В цикле repeat ... until eof(f) будем производиться считывание строк из файла model.txt:

readln(f,s);
s:=trim(s);

 

6.Как только будет замечена строка со словом face, мы увеличим длину массива Poligoni на единицу:

setlength(Poligoni,length(Poligoni)+1); // увеличиваем длину массива

 

7. Индекс нового полигона в массиве определить очень просто - это длина массива минус один (минус один, так как полигоны в массиве имеют индекс, начинающийся с нуля):

j:=length(Poligoni);

 

8. Теперь будем считывать данные из файла и записывать их в j-тый полигон:

readln(f,s);
Poligoni[j-1].BMPname:=trim(s); // имя текстуры readln(f,s);
poligoni[j-1].vertnum:=StrToInt(trim(s)); // количество вершин полигона for i:=0 to Poligoni[j-1].vertnum-1 do // считываем вершины полигона
begin
readln(f,s);
s:=trim(s);
Poligoni[j-1].vert[i].x:=StrToFloat(StringWordGet(s,' ',1));
Poligoni[j-1].vert[i].y:=StrToFloat(StringWordGet(s,' ',2));
Poligoni[j-1].vert[i].z:=StrToFloat(StringWordGet(s,' ',3));
end; for i:=0 to Poligoni[j-1].vertnum-1 do // считываем нормали полигона
begin
readln(f,s);
s:=trim(s);
Poligoni[j-1].norm[i].x:=StrToFloat(StringWordGet(s,' ',1));
Poligoni[j-1].norm[i].y:=StrToFloat(StringWordGet(s,' ',2));
Poligoni[j-1].norm[i].z:=StrToFloat(StringWordGet(s,' ',3));
end; for i:=0 to Poligoni[j-1].vertnum-1 do // считываем текстурные координаты
begin
readln(f,s);
s:=trim(s);
Poligoni[j-1].texcoord[i].u:=StrToFloat(StringWordGet(s,' ',1));
Poligoni[j-1].texcoord[i].v:=StrToFloat(StringWordGet(s,' ',2));
end;

9. При загрузке текстур мы сделаем проверку, позволяющую избежать повторной загрузки текстур:

for i:=0 to length(Poligoni)-1 do // для всех полигонов
begin loaded:=false; // это для алгоритма проверки дублирующихся текстур for j:=0 to i-1 do // посмотрим полигоны, которые // уже проходили этап загрузки // текстур
if Poligoni[j].BMPname=Poligoni[i].BMPname then //если имена совпадают, // то загружать заново не нужно
begin
loaded:=true; // говорим, что текстура уже загружена
Poligoni[i].gltex:=Poligoni[j].gltex;// записываем ID // загруженной ранее текстуры
break; //прекращаем цикл for (выходим из цикла)
end; if not loaded then // если всё-таки не загружена, то загрузим:
begin
LoadTexture(Poligoni[i].BMPname, Poligoni[i].gltex);
end; end;

 

10. В учебных целях вывод на экран загруженной модели сделаем максимально просто:

for i:=0 to length(poligoni)-1 do
begin
glBindTexture(GL_TEXTURE_2D,poligoni[i].gltex);
glBegin(GL_POLYGON);
for j:=0 to poligoni[i].vertnum-1 do
begin
glTexCoord2f(poligoni[i].texcoord[j].u, poligoni[i].texcoord[j].v);
glNormal3f(poligoni[i].norm[j].x, poligoni[i].norm[j].y, poligoni[i].norm[j].z);
glVertex3f(poligoni[i].vert[j].x, poligoni[i].vert[j].y, poligoni[i].vert[j].z);
end;
glEnd;
end;

Итак, мы создали модель в Blender-е, написали экспортер и загрузили в своей пограмме на Delphi. Вот два маленьких скриншота: сначала - из Blender-а:

 

А вот что отобразилось в нашей программе на Delphi с помощью OpenGL:

 

В заключении могу сказать, что Blender - отличная вещь для создания игр с помощью OpenGL и Delphi!