Информатика и образование
  Мобильная версия сайта            
               
[Главная] [Новости]
[Статьи]
[Проекты]
[Ссылки]
[Автор]
               
    [Архив новостей]        
               
  [Форум] на форуме можно задать вопрос, посмотреть ответы на часто задаваемые вопросы  
       
  Здравствуйте! Вы попали на информационно-образовательный сайт посвященный информатике, информационным технологиям и компьютерным играм. Подробнее о целях и задачах сайта в разделе Главная. [English version of this page here...]    
       
  [Базовые уроки по DirectX] [Основы DirectMusic на Delphi] [Основы DirectInput8 на Delphi] [Основы DirectSound8 на Delphi]    
  [Разработка компьютерной игры] [Пример игры Donuts3D] [Delphi DirectX]    
       
  Основы 3D-программирования DirectX8.1 в Delphi 6-7: теоретические и практические основы создания игр.    
       
  Анимация    
       
 

10.01.2008

Эти заметки написаны с целью лучшего уяснения работы с шейдерами, а также для внедрения использования анимации в имеющийся код.

   
       
  [Все уроки]    
       
  За основу взят пример SkinnedMesh.    
       
 

Итак, внутри DrawFrames происходит следующее:

Result:= m_pd3dDevice.SetTransform(D3DTS_WORLD, pframeCur.matCombined);

по матрице matCombined устанавливается Мировая трансформация;

Result:= DrawMeshContainer(pmcMesh); //строится анимированный меш

Result:= DrawFrames(pframeChild, cTriangles);//осуществляется рекурсивный вызов данной процедуры для дочерних кадров

   
       
 

главная работа происходит внутри DrawMeshContainer(pmcMesh);

поступивший сюда меш (pmcMesh) проходит следующие стадии обработки:

if (m_method <> pmcMesh.m_Method) then GenerateMesh(pmcMesh); //изменился метод анимации - нужно сгенерировать новый меш

Далее осуществляется построение в зависимости от выбранного метода анимации:

if (m_method = D3DNONINDEXED) then

Получаем указатель на массив комбинаций костей скелета влияющих на вершины модели. Группы вершин связанных с данными комбинациями костей (в простейшем случае это всего лишь 1 кость) имеют Атрибуты Идентификации (AttribId)

По этим атрибутам происходит определение числа фаз межкадрового сглаживания - numBlend,

из массива Комбинаций костей считываются матрицы трансформации по их идентификатору -

matid:= pBoneComb[ipattr].BoneId[i]; //ipattr - это номер атрибута, i - номер фазы сглаживания

//из массива Комбинаций Костей по номерам считываются матрицы и устанавливаются в качестве Мировой матрицы трансформации. Перетекание между фазами анимации осуществляется за счет перемножения матриц

m_pd3dDevice.SetTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneMatrix[matid]^);
m_pd3dDevice.MultiplyTransform(D3DTS_WORLDMATRIX(i), pmcMesh.m_pBoneOffsetMat[matid]);

   
       
 

Далее происходит установка количества фаз межкадрового сглаживания, устанавливается материал, текстура и происходит фактическая отрисовка подмножества граней меша с данным номером атрибута

Если графическое устройство не может производить все фазы межкадрового сглаживания на аппаратном уровне (HW - Hardware Vertex Processing), то далее оставшаясь часть модели отрисовывается на программном уровне. В коде это отражается лишь установкой соответствующего флага и появлением новой группы атрибутов (iAttrSplit)

   
       
  Анимация при помощи других методов осуществляется подобным же образом с особенностями реализации для конкретного ее вида (см исходный код).    
       
  Ответ на первый вопрос: 1) Каким образом внедрить в код примера другие модели (например, земную поверхность)?    
       
 

Если создать отдельный класс, например TAnimatedModel, реализующий возможности заложенные в примере SkinnedMesh, то просто создав отдельный объект и загрузив в него нужную модель можно внедрить в код использование других моделей.

Но вот в чём загвоздка: в примере SkinnedMesh даже написанном на C++ из DX SDK содержится следующая ошибка - при попытке загрузить модель через пункт меню Open приложения возникает ошибка. Поэтому прежде чем двигаться дальше нужно найти причину и устранить эту ошибку!!!

   
       
  Заметки по устранению:
При загрузке модели приложением ошибка не возникает. Как проходит загрузка
во время старта приложения?

В методе создания (Create) приложения:

SomeModel := TAnimatedModel.Create;

В методе инициализации (InitDeviceObjects):

SomeModel.m_szPath := 'Media\Models\nanozone1.x';
SomeModel.InitDeviceObjects(m_pd3dDevice);

   
       
  В методе RestoreDeviceObjects методы SomeModel не вызываются.    
       
 

В методе Invalidate -

SomeModel.InvalidateDeviceObjects;

   
       
 

В методе DeleteDeviceObjects -

SomeModel.Free;

   
       
 

В методах Render и FrameMove соответстующие методы SomeModel

При выборе пункта меню Open приложения SkinnedMesh осуществляется следующая цепочка команд (см. метод MsgProc):

{...}

lstrcpy(m_szPath, ofn.lpstrFile);
FreeAndNil(SomeModel);
SomeModel := TAnimatedModel.Create;
lstrcpy(SomeModel.m_szPath, m_szPath);
//hr:= SomeModel.LoadMeshHierarchy;
hr:= SomeModel.InitDeviceObjects(m_pd3dDevice);
if FAILED(hr) then
MessageBox(0, 'Could not open file or incorrect file type',
'Error loading file', MB_OK);

   
       
 

Ошибка происходит внутри:

SomeModel.InitDeviceObjects > LoadMeshHierarchy

в строках кода

// Enumerate top level objects.
// Top level objects are always data object.
while (SUCCEEDED(pxofenum.GetNextDataObject(pxofobjCur))) do
begin
Result:= LoadFrames(pxofobjCur, pdeMesh, dwOptions, m_dwFVF,
m_pd3dDevice,
pdeMesh.pframeRoot);
pxofobjCur:= nil;

if FAILED(Result) then Exit;
end;

на втором проходе цикла

   
       
  код ошибки: -2005529767 Просмотр расшифровки кода ошибки утилитой DXErr из DX SDK дает следующую расшифровку кода ошибки: D3DXERR_INVALIDDATA    
       
 

В режиме отладки проследим за поведением приложения при

1) начальном старте

2) при выборе пункта меню Open

   
       
 

1) начальный старт -

SomeModel.InitDeviceObjects > LoadMeshHierarchy

   
       
     
       
  Для полной загрузки модели nanoco_go.x требуется всего 3 прохода цикла внутри которого установлена контрольная точка (см. рисунок) без учета рекурсивных вызовов происходящих внутри процедуры LoadFrames    
       
 

2) при выборе пункта меню Open

На втором проходе цикла возникает указанная выше ошибка

   
       
  Достичь выявления сути ошибки можно сравнением внутренней работы, происходящей внутри LoadFrames при 1) начальном старте приложения и при 2) выборе пункта меню Open    
       
  Сначала установим на каком рекурсивном вызове происходит ошибка при условии 2 внутри LoadFrames    
       
 

Ошибка возникает на 2 рекурсивном вызове.

Теперь нужно установить на каком проходе возникает ошибка внутри данного рекурсивного вызова.

Внутри данного рекурсивного вызова ошибка возникает в строке

if SUCCEEDED(Result) then
begin
Result:= LoadFrames(pxofobjChild, pde, options, fvf, pD3DDevice, pframeCur);
if FAILED(Result) then Exit;

pxofobjChild:= nil;
end;

pxofChild:= nil;
end;
end;
except
on EOutOfMemory do Result:= E_OUTOFMEMORY;
else raise;
end;

   
       
  на 2-ом проходе, другими словами это можно записать схемой (в скобках указан номер прохода, на котором происходит ошибка при условии 2 - второй столбец таблицы)    
       
   

1) начальный старт приложения

LoadMeshHierarchy > LoadFrames (3)

 

2) загрузка выбором пункта меню Open

LoadMeshHierarchy > LoadFrames (2)

> LoadFrames (2) > LoadFrames (2) > LoadMesh

ошибка происходит в строке

Result:= D3DXCreateTextureFromFile(m_pDevice, szPath, pmcMesh.pTextures[iMaterial]);
if FAILED(Result) then Exit;

   
       
 

При начальном старте приложения строка кода

DXUtil_FindMediaFile(szPath, pMaterials[iMaterial].pTextureFilename);

производит определение пути к файлу с текстурой, а строка

Result:= D3DXCreateTextureFromFile(m_pDevice, szPath, pmcMesh.pTextures[iMaterial]);

осуществляет загрузку текстуры

например:

pMaterials[iMaterial].pTextureFilename = 'Media/Textures/nanoco.jpg'

strFullPath (внутри DXUtil_FindMediaFile) = полный путь к файлу

При загрузке через Open строка кода

DXUtil_FindMediaFile(szPath, pMaterials[iMaterial].pTextureFilename);

производит определение пути к файлу с текстурой, но внутри данной процедуры происходит ошибка из-за построения некорректного пути.

Эта ошибка присуща не только примеру SkinnedMesh, но и другим примерам из DX SDK написанным на C++: enchancedmesh, optimizedmesh, progressivemesh и проявляется в отсутствии текстуры на модели открытой через пункт меню Open.

   
       
  Разработчики DirectX в DX SDK 9 на обновленном примере SkinnedMesh проблему обошли за счёт удаления пункта меню Open.    
       
 

Но вернемся к нашему примеру.

Если в примере SkinnedMesh внутри процедуры LoadMesh пару строк заменить на

{** Result:= }D3DXCreateTextureFromFile(m_pDevice, szPath, pmcMesh.pTextures[iMaterial]);
//** if FAILED(Result) then Exit;

То открытие моделей можно будеть осуществлять, только с потерей вида текстуры на модели (т.е будет наблюдаться такое же поведение как в примерах enchancedmesh, optimizedmesh, progressivemesh).

   
       
 

В приложениях для просмотра мешей mview (MeshView) из DX SDK 8 и DXViewer из DX SDK 9 все модели загружаются без текстур. Это говорит о том, что в данном случае имеет место какая-то фундаментальная проблема которая в примерах из DX SDK никак не решается, а просто обходится стороной. Возможно это связано с особенностями COM-приложений и характером связей между файлом модели и файлами текстур.

Поэтому и нам придется похоже смириться с этим и радоваться тому, что текстуры хотя бы загружаются при начальном старте приложения.

   
       
 

Я попытался разрешить проблему своими силами добавив в коде приложения SkinnedMesh следующий код ограниченный строками //** означающими, что код отладочный и его в реальном приложении следует удалить или закомментировать:

//**
procedure ExtractFileName2(var FileName: PChar);
const
Delimeter = '/';
var
tmpS: PChar;

begin
tmpS := StrRScan(FileName, Delimeter);
StrCopy(FileName, @tmpS[1]);
end;

procedure RemakeDelimeter(var AStr: array of Char);
const
Delimeter = '/';
Delimeter2= '\';
var
i: Integer;
begin
for i:=0 to StrLen(AStr)-1 do
if AStr[i] = Delimeter2 then AStr[i] := Delimeter;
end;
//**

А внутри метода LoadMesh внёс следующие изменения:

if (pMaterials[iMaterial].pTextureFilename <> nil) then
begin
//**
ZeroMemory(@szPath, SizeOf(szPath));
GetMem(tempStr, MAX_PATH-1);
StrCopy(tempStr, pMaterials[iMaterial].pTextureFilename);

if OpenMenuItemSel then begin
ExtractFileName2(tempStr);
//DXUtil_FindMediaFile2(szPath, tempStr);
GetModuleFileName(0, szPath, SizeOf(szPath));
ModulePath := ExtractFilePath(szPath);
ZeroMemory(@szPath, SizeOf(szPath));
StrPCopy(szPath, ModulePath);
RemakeDelimeter(szPath);
StrCat(szPath, pMaterials[iMaterial].pTextureFilename);
end;

StrCopy(szPath, tempStr);

FreeMem(tempStr);
//**
//DXUtil_FindMediaFile(szPath, pMaterials[iMaterial].pTextureFilename);


Result:= D3DXCreateTextureFromFile(m_pDevice, szPath, pmcMesh.pTextures[iMaterial]);
if FAILED(Result) then Exit;

   
       
 

Таким образом:

- Я пробовал загружать текстуру без использования процедуры DXUtil_FindMediaFile;

- Определять путь к текстуре через другие методы;

- Менять разделители в пути к текстуре с '\' на '/' и наоборот;

Но это не помогло решить проблему.

   
       
 

Поэтому остается смириться с этой проблемой убрав весь отладочный код и оставив только строки

{** Result:= }D3DXCreateTextureFromFile(m_pDevice, szPath, pmcMesh.pTextures[iMaterial]);
//** if FAILED(Result) then Exit;

А также учитывать факт потери текстур моделями при их загрузке не во время начального старта приложения, а повторно до его закрытия. Нормальное появление текстур на моделях возможно только при рестарте приложения!

   
       
 

21.01.2008

Однако в этом случае возникает вопрос: Как же происходит повторная загрузка моделей в играх? Ведь ни одна игра не может загружать все модели для всех уровней разом. В разных уровнях требуются разные модели, а значит неминуемы повторные загрузки. И это надо осуществить без рестарта приложения!

Решение найдено!!!

Если внутри файла модели указан не относительный, а абсолютный путь к текстуре в данной системе, то проблема отпадает и указанные выше ограничения и изменения в коде снимаются!!!

   
       
  Таким образом, решением может быть коррекция путей к текстурам в файлах моделей на каждой конкретной системе. Это должно происходить во время инсталляции игры или самим игровым приложением до выполнения загрузки моделей.    
       
  22.01.2008
- решение проблемы оказалось тривиально простым, даже не потребуется
написания ни единой строки кода!!!

Если модель имеет следующий путь Media\Models\SomeModel.x
Тогда если текстура находится в Media\Textures\SomeTex.jpg
путь к текстуре нужно записать так: ..\Textures\SomeTex.jpg

При этом все описанные проблемы снимаются!!! и никаких изменений в коде делать не нужно!!!

   
       
 

24.01.2008

Эксперименты показали еще одну особенность. Это касается кода приложений использующих DirectX 8.1.

Если модель расположена по пути -
Media\Models\nx4_2.x

А текстура -
Media\Textures\nx4_2.bmp

То если внутри модели указан путь к текстуре как
Media/Textures/nx4_2.bmp

То при старте приложения текстура выводится,
при повторном открытии приложения - нет.

Если путь записан как
../Textures/nx4_2.bmp

то при старте приложения текстура не
выводится, при повторном открытии модели выводится.

В приложениях DirectX 9 этого не наблюдается. Путь всегда должен быть записан как ../Textures/nx4_2.bmp

   
       
 

31.01.2008

Необходима переделка пути только при первом
старте приложения.

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

была объявлена глобальная переменная
var
FirstStart: Boolean = True;

Внутри метода загрузки моделей -

// Find the path to the texture and create that texture

if FirstStart then begin
TexturesList := TStringList.Create;
TexturesList.LoadFromFile(SpaceBoxModelTextures);
// для конкретной модели нужно указать конкретное имя

// текстового файла списка используемых текстур

StrCopy(strMediaPath, PChar(TexturesList[i]));
end
else
DXUtil_FindMediaFile(strMediaPath, d3dxMaterials[i].pTextureFilename);

и в конце данного метода -

if FirstStart then begin
TexturesList.Free;
FirstStart := False;
end;

Идея решения проблемы состоит в том, что в папке с моделями
помимо самих моделей хранятся текстовые файлы совпадающие
с именами моделей или отличающиеся от них суффиксом, например

исходная модель - spacebox.x
текстовый файл содержащий пути к текстурам данной модели, используемый
при первом запуске приложения - spacebox_textures.txt
Внутри данного файла записан ряд строк, содержащих абсолютные пути
к текстурам, т.е.

Если модель имеет следующий путь Media\Models\SomeModel.x
Тогда если текстура находится в Media\Textures\SomeTex.jpg
путь к текстуре записан как: Media\Textures\SomeTex.jpg

Номера строк внутри файла списка текстур соответствуют индексам
материалов цикла обработки материалов.

Как нужно записывать пути к текстурам для анимированных или
сложных моделей будет решено далее.

Т.о. внутри модели путь к текстуре должен быть относительный -
например ..\Textures\SomeTex.jpg - это будет гарантировать, что
модель будет текстурирована при второй и последующих загрузках;

появление текстуры при первой загрузке (при старте приложения)
будет гарантироваться наличием указанного текстового файла, содержащего
абсолютные пути в виде -
Media\Textures\SomeTex.jpg

   
       
 

[далее - решение поставленных выше вопросов -]

1) Каким образом внедрить в код примера другие модели (например, земную поверхность)?

2) Как использовать в коде примера камеру ?

3) И наконец, как перемещать и вращать анимированную модель в 3D-сцене, например чтобы анимированный персонаж перемещался по земной поверхности ?

   
       
  [Назад] [Все уроки]    
       
       
       
 

Обновления и новости о развитии Delphi DirectX проекта
смотри на сайтах:

http://www.megainformaticsite.pochta.ru

http://www.megainformatic.boom.ru

http://www.megainformatic.narod.ru

 

   
       
     
 

по всем вопросам пишите на megainformatic@mail.ru или оставьте сообщение на форуме

 
     
   Обмен ссылками  
     
     
             
 
 
         
(с) МЕГА ИНФОРМАТИК 2006-2009
Hosted by uCoz