[Главная] [Новости] [Статьи] [Игры] [Проекты] | [Автор] | |||
Основы 3D-программирования DirectX8.1 в Delphi 6-7: теоретические и практические основы создания игр. | ||||
Отображение
моделей и работа с камерой |
||||
25.01.2008 Заметки посвящены проблемам отрисовки статичных моделей (мешей) и работе со свободной камерой CD3DCamera использующей возможности CD3DArcBall и управление от DirectInput |
||||
[Все уроки] | ||||
Данные заметки покажут Вам - способ описания проблемы для лучшего уяснения самому себе; - загрузку и отображение статичных мешей; использование возможностей ArcBall для целей свободного управления игровой камерой. | ||||
Для выполнения упражнений Вам понадобится: - архив с исходным кодом; (1,84 Mb) - архив с ОБЩИМ КОДОМ; (772 Кб) |
Архив с исходным кодом содержит пример решения поставленной задачи, архив с общим кодом - содержит дополнительный общий код необходимый при разработке Delphi DirectX 8.1 - приложений. | |||
За основу взят пример OpenMesh. | ||||
В методе Create приложения (CMyD3DApplication.Create) Задаем путь для загружаемой при старте приложения модели m_strMeshFilename:= 'Media\Models\scene1\spacebox0.x'; Создаем объект для камеры (пока вместо камеры будет ArcBall!) m_ArcBall := CD3DArcBall.Create; |
||||
В методе InitDeviceObjects приложения (CMyD3DApplication.InitDeviceObjects) Производим загрузку модели посредством интерфейса ID3DXMesh и ее текстур Здесь же производим вычисление сферы ограничивающей модель (D3DXComputeBoundingSphere) и вычисляем нормали (если они отсутствуют). Нормали будут нужны, т.к. при отображении будет использоваться освещение (см. метод CMyD3DApplication.RestoreDeviceObjects) |
||||
В методе RestoreDeviceObjects приложения (CMyD3DApplication.RestoreDeviceObjects) Происходит установка необходимых состояний, создание источника света Установка вычисленного ранее радиуса ограничивающей сферы для ArcBall Установка проекционной трансформации |
||||
В методе Render приложения осуществляется отрисовка (отображение) с использованием примененных к модели материалов | ||||
В методе FrameMove приложения осуществляется - D3DXMatrixTranslation(matWorld,
-m_vObjectCenter.x,
D3DXMatrixMultiply(matWorld, matWorld, m_ArcBall.GetRotationMatrix^);
//установка трансформаций от ArcBall
// Set up view matrix |
||||
Использование поведения ArcBall для работы с камерой CD3DCamera и управления от DirectInput (компонент TDXInput из модуля DX8_DIUtil8.pas) За основу был взят метод CD3DArcBall.HandleMouseMessages Нужно адаптировать его для указанных целей. Нажатия клавиш мыши или любых других кнопок регистрируются элементарно. Отслеживание перемещений мыши осуществляется следующим образом: Поскольку m_DXInput.Mouse.X и m_DXInput.Mouse.Y отображают только моментальное перемещение мыши, то использование индикатора m_UserInput.bMove
:= not ((m_DXInput.Mouse.X = 0) or не позволяет вызывать код в строках - if
m_UserInput.bMove then {...} Можно накапливать моментальные перемещения мыши следующим образом - m_UserInput.MouseMoveX
:= m_UserInput.MouseMoveX + m_DXInput.Mouse.X; |
||||
При этом отслеживать перемещение можно запоминая последнее перемещение и сравнивая его с текущим. Отсутствие изменений будет свидетельствовать об отсутствии перемещения - iMouseX
:= m_UserInput.MouseMoveX; m_UserInput.bMove
:= not ((iMouseX = iCurMouseX) or |
||||
if
m_UserInput.bMove then begin iCurMouseX:= iMouseX; iCurMouseY:= iMouseY; end; |
||||
Для реализации перемещений от правой и средней кнопок мыши пришлось сделать: if
m_UserInput.bRMouseBtn or m_UserInput.bMMouseBtn then
//if (Msg.wParam and MK_RBUTTON) = MK_RBUTTON then
// Store mouse coordinate |
||||
Т.е. зависимость от индикатора bMove пришлось убрать, для перемещений использовать значения -m_DXInput.Mouse.X, -m_DXInput.Mouse.Y (минус - для смены направлений). | ||||
Как осуществляется работа по вращениям от левой кнопки мыши внутри CD3DArcBall.HandleMouseMessages? | ||||
Записывается текущее состояние мыши - iMouseX
:= LOWORD(Msg.lParam); Если нажата левая кнопка - m_bDrag:=
True; Если отпущена левая кнопка - m_bDrag:= FALSE; Exit; Если мышь перемещается - то если установлен индикатор m_bDrag //
recompute m_qNow
D3DXMatrixRotationQuaternion(m_matRotation, m_qNow); //
определение матрицы вращения по кватерниону //
Store mouse coordinate |
||||
момент вращения в коде нигде не используется, поэтому его можно смело убрать //
recompute m_qNow
D3DXMatrixRotationQuaternion(m_matRotation, m_qNow); //
определение матрицы вращения по кватерниону
|
||||
В итоге получается следующее: - В момент нажатия LMB формируется вектор s_vDown по координатам iMouseX, iMouseY и запоминается текущая ориентация (m_qDown) ; - В момент перемещений мыши с нажатой LMB определяется текущий вектор vCur, определяется ориентация между положением в момент нажатия LMB и в момент перемещений с LMB.
|
||||
Остаются следующие трудности: вращение происходит не слишком послушно перемещениям мыши. Это проявляется в том, что вращение несогласовано с движениями мыши - слишком быстро и не зависит от направления перемещения. Очень трудно подобрать нужную позицию мыши, чтобы осуществить изменение ориентации не только в плоскости, но и в пространстве. В случае с ArcBall это происходит легко и непринужденно. Что важно для определения ориентации? if
m_UserInput.bLMouseBtn then if
m_UserInput.bMove then D3DXQuaternionMultiply(m_qNow, m_qNow, qAxisToAxis); D3DXMatrixRotationQuaternion(m_matRotation, m_qNow); end; end; Введение в код учета момента времени fTimeLapsed := DXUtil_Timer( TIMER_GETELAPSEDTIME ); s_vDown:=
ScreenToVector(Round(iMouseX*fTimeLapsed), улучшает ситуацию в плане согласованности от перемещений мыши. Причем введение любых дополнительных коэффициентов - iMouseX*fTimeLapsed*10000 или iMouseX*fTimeLapsed*0.0001 на ситуацию никак не влияет. |
||||
Для улучшения изменений ориентации нужно сравнить насколько должны отличаться друг от друга векторы s_vDown и vCur на которые непосредственно влияют значения координат мыши в моменты времени формирования этих векторов. | ||||
s_vDown получается медленно изменяемой величиной (с точки зрения мнгновенных моментов времени) - s_vDown:= ScreenToVector(iMouseX, iMouseY); vCur
более быстро изменяемая величина, но диапазон значений очень узок из-за
того, что берутся мнгновенные значения перемещений мыши - vCur:=
ScreenToVector(m_DXInput.Mouse.X, Если для vCur использовать те же принципы изменения величины (т.е. iMouseX, iMouseY), то опять получаем слишком несогласованные и быстрые вращения - vCur:= ScreenToVector(iMouseX, iMouseY); Если
для s_vDown использовать принципы изменения для мнгновенных перемещений
- s_vDown:=
ScreenToVector(m_DXInput.Mouse.X, |
||||
Введение в код отладочной строки выводящей текущие значения векторов для ArcBall и для GameCamera - в методе CMyD3DApplication.Render - {[m_ArcBall.m_vDown.x,
m_ArcBall.m_vDown.y, m_ArcBall.m_vDown.z, позволило установить ряд заблуждений, которые были успешно выявлены и исправлены. |
||||
- формирование вектора s_vDown должно происходить не в момент нажатия, а в момент отпускания левой кнопки мыши (LMouseButtonUp); - формирование вектора vCur должно происходить когда мышь движется с нажатой LMB (LMouseButtonDown); - если в качестве координат курсора мыши использовать накапливаемые моментальные перемещения, то ощущается значительная несогласованность вращений. Это связано с тем, что данные координаты имеют знак и не согласованы с координатами в границах окна. Проблема решается очень просто - Windows.GetCursorPos(MouseCursorPos); Т.е. если использовать системные координаты курсора приведенные к размерам окна приложения, а в соотвествующих координатах векторов использовать их - s_vDown := ScreenToVector(MouseCursorPos.X, MouseCursorPos.Y); {...} vCur := ScreenToVector(MouseCursorPos.X, MouseCursorPos.Y); То мы получим полное ощущение полноценных перемещений и вращений как и в случае с ArcBall. |
||||
Важные следствия вытекающие из результатов данной работы: 1) в примере с организацией перемещений курсора мыши по экрану (создание игрового меню) можно использовать не только функцию текущих координат мыши, но и использовать собственный учет координат через переменные - (Можно накапливать моментальные перемещения мыши следующим образом) - m_UserInput.MouseMoveX
:= m_UserInput.MouseMoveX + m_DXInput.Mouse.X; 2) поскольку в примере SkinnedMesh также используется ArcBall, то его легко переделать под использование камеры CD3DCamera в соответствии с особенностями описанными нами выше. |
||||
Код данного примера поможет Вам разобраться как происходит переделка возможностей ArcBall под видоизмененную CD3DCamera - камеру. | ||||
Что нужно сделать с кодом, чтобы работал ArcBall | Что нужно сделать с кодом, чтобы работала камера | |||
в методе CMyD3DApplication.MsgProc - раскомментировать строку кода - //** m_ArcBall.HandleMouseMessages(hWnd, uMsg, wParam, lParam); в методе CMyD3DApplication.RestoreDeviceObjects - раскомментировать строку кода - //** SetupArcBall; а строку кода SetupCamera; закомментировать - //SetupCamera в методе CMyD3DApplication.FrameMove - раскомментировать строку кода - //** MoveArcBall; а строки UpdateInput; MoveCamera; - закомментировать - //UpdateInput; //MoveCamera;
|
Вернуть произведенные изменения к исходному виду | |||
Не забудьте также после проделанных изменений перекомпилировать код!!! | ||||
[Назад] [Все уроки] | ||||
[Главная] [Новости] [Статьи] [Игры] | ||||
(c) Мега Информатик 2006-2008 |