Уголок DirectX        Содержание

Build on 14.06.2k
(c)2000 "Visual Basic meet DirectX"

Простое полноэкранное приложение

В этой статье мы наконец-то что-то нарисуем. Мало того, мы заставим это что-то еще и крутиться!

Я очень надеюсь, что вы прочитали и выполнили все, о чем я говорил в прошлой статье, то есть у вас уже есть "зародыш" модуля mdlDirect3DRM, а также "бесподобная универсальная форма выбора драйвера и метода рендеринга 8-)" Если есть, то очень хорошо, ежели нет, тогда либо прочитайте сейчас, либо в моменте создания самых главных объектов в этой части вам будет несколько тяжко.

Итак. Что мы будем сейчас творить. Во-первых, наше приложение будет работать в полноэкранном режиме. Оконный режим я освещу в следующих статьях, а пока будем работать в полном экране 640x480x16. Во-вторых, мы не будем пока загружать x-файлы (это не Секретные Материалы - это формат такой) и не будем возиться с текстурами, как бы вам этого не хотелось. Потерпите до следующих статей. Главное понять основы. Мы соорудим из четырех треугольников-поверхностей пирамиду. Почти египетскую, только все ее стороны будут разного цвета. Добавим в сцену освещение и камеру.

Начнем же.

Начнем мы с собирания нового проекта из уже существующих частей. Создайте новую папку и скопируйте туда форму frmDriver ("бесподобная универсальная форма выбора..."), а также mdlDirect3DRM, которые остались (ведь остались?) у вас от предыдущего проекта.

Зайдите в Visual Basic, добавьте к проекту библиотеки DirectX7 и Win32. Главную форму назовите frmMain, а стартовой сделайте процедуру Sub Main. К проекту добавьте frmDriver и mdlDirect3DRM. Все получившееся сохраните в новом проекте.

Начнем написание нового кода с модуля. К уже существующим добавьте новые определения. Все должно выглядеть теперь так:

Public dx As New DirectX7 'Объект DirectX
Public dd7 As DirectDraw7 'Объект DD для оконного режима
Public dd4 As DirectDraw4 'Объект DD для полноэкранного режима
Public d3d As Direct3D7 'Объект D3D:IM
Public d3dRm As Direct3DRM3 'Объект D3D:RM
Public ddsd As DDSURFACEDESC2 'Описание поверхности
Public Caps As DDSCAPS2 'Описание аппаратных возможностей
Public Device As Direct3DRMDevice3 'Устройство рендеринга
Public Scene As Direct3DRMFrame3 'Фрейм сцены
Public Camera As Direct3DRMFrame3 'Фрейм камеры
Public World As Direct3DRMFrame3 'Фрейм объектов
Public MeshBuilder As Direct3DRMMeshBuilder3 'Конструктор объединений
Public dds4Primary As DirectDrawSurface4 'Главная поверхность для DD4
Public dds4Back As DirectDrawSurface4 'Задний буфер для DD4
Public Viewport As Direct3DRMViewport2 'Объект Viewport

Public driverGuid As String 'Используемая видеокарта
Public renderGuid As String 'Используемое устройство рендеринга видеокарты

Public Running As Boolean 'Программа работает?

Вы видите много новых и уже знакомых объектов. Пока не волнуйтесь, чтобы все понять сразу. По мере протекания процесса, вы познакомитесь со всеми ними.
Продолжим написание модуля. Теперь надо собрать проект так, чтобы работала сначала форма выбора драйвера, а затем включалась главная форма. Этим займется функция Sub Main.

'-----======= Стартовая точка ======--------
Private Sub Main()
frmDriver.Show vbModal 'Выбрать драйвер
frmMain.Show 'Запускаем программу
End Sub

Теперь, мы напишем процедуру, которая инициализирует Direct3D:RM в полноэкранном режиме. Этой процедуре надо будет передать разрешение экрана и форму, на основе которой создается Direct3D.

Direct3D:RM в полноэкранном режиме создается на основе поверхности DirectDraw. Основная концепция такова: вы сперва должны создать обыкновенную цепь флиппинга, как это делаете с любым приложением DirectDraw. Единственное отличие здесь состоит в том, что во-первых, мы будем использовать объекты 4-й версии (Direct3D:RM работает только с DirectX6), а во-вторых, объект DirectDraw мы будем создавать для выбранного GUID видеокарты.

Public Sub InitFullscreen(hWnd As Long, sX As Long, sY As Long, sBitDepth As Long)
  'Стандартное создание DirectDraw4 под выбранный драйвер с главной поверхностью
  'и одним задним буфером

  Set dd4 = dx.DirectDraw4Create(driverGuid)
  Call dd4.SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE Or DDSCL_FULLSCREEN Or   DDSCL_ALLOWREBOOT)
  Call dd4.SetDisplayMode(sX, sY, sBitDepth, 0, DDSDM_DEFAULT)
  ddsd.lFlags = DDSD_BACKBUFFERCOUNT Or DDSD_CAPS
  ddsd.ddsCaps.lCaps = DDSCAPS_COMPLEX Or DDSCAPS_FLIP Or   DDSCAPS_PRIMARYSURFACE Or DDSCAPS_3DDEVICE
  ddsd.lBackBufferCount = 1
  Set dds4Primary = dd4.CreateSurface(ddsd)
  Caps.lCaps = DDSCAPS_BACKBUFFER Or DDSCAPS_3DDEVICE
  Set dds4Back = dds4Primary.GetAttachedSurface(Caps)

После этого, мы создаем объекты Direct3D:RM Главным объектом, является (помимо самого объекта D3DRM) устройство рендеринга. Этот объект выполняет рендеринг вашего изображения с указанными параметрами - качеством текстур, методом затенения и т.п. Мы этого пока задавать не будеи и оставим все как есть по умолчанию. Затем мы должны создать фрейм сцены. На фреймах построено все моделирование в Direct3D:RM Фрейм - это некая поверхность, на которой находятся объекты. Главный фрейм - сцена - длжен быть всегда. Также, вы обязательно должны создать фрейм камеры (иначе что вы увидите). Затем, вы создаете свои фреймы для ваших объектов. В D3D вы передвигаете не объекты по сцене, а фреймы с "укрепленными" на ними объектами. После создания главных фреймов, надо создать объект Viewport. Это как-бы ваш экран в новый 3D-мир. Viewport накладывается на сцену и определяет что вы видите в двухмерном понимании этого слова.

  'Создание Direct3D:RM
  Set d3dRm = dx.Direct3DRMCreate()
  Set Device = d3dRm.CreateDeviceFromSurface(renderGuid, dd4, dds4Back,   D3DRMDEVICE_DEFAULT)
  Set Scene = d3dRm.CreateFrame(Nothing)
  Set Camera = d3dRm.CreateFrame(Scene)
  Set Viewport = d3dRm.CreateViewport(Device, Camera, 0, 0, sX, sY)

В параметрах при создании фрейма сцены мы указали Nothing, а у камеры - Scene. Это значит, что фрейм сцены не относится ни к чему, а камера относится к сцене. В этом "отношении" заключается иерархия фреймов в 3D-мире. Viewport мы "накладываем" на камеру и делаем его размером во весь экран. С созданием устройства рендеринга вроде все ясно: мы создаем его на основе заднего буфера, то-есть рендеринг будет производиться на него. С созданием объекта D3DRM вообще проще не придумаешь.

Мы допишем сюда еще две строчки, хотя по хорошему, они не относятся к обязательным начальным установкам. Во-первых, мы создадим вещественный свет. То есть свет который издают сами поверхности. Во-вторых, мы создадим объект MeshBuilder. Это очень интересный объект. По своей сути, он представляет собой конструктор Лего :) С помощью его методов, вы создаете объединение поверхностей, то есть ваш объект. Так как объект у нас один, то и конструктор объединение то же один.

  Scene.AddLight d3dRm.CreateLight(D3DRMLIGHT_AMBIENT, dx.CreateColorRGB(1, 1, 1))
  Set MeshBuilder = d3dRm.CreateMeshBuilder
End Sub

Раз создали, надо и позаботиться об уничтожении. Эта функция уничтожит все заданные объекты.

Public Sub DestroyEverything()
Set Viewport = Nothing
Set Device = Nothing
Set d3dRm = Nothing
Set dd4 = Nothing
Set dd7 = Nothing
End Sub

Следующую функцию я взял из mdlDirectDraw7 и переделал под DD4. Она потребуется нам для очистки заднего буфера. Ведь перед тем, как отрендерить новый кадр, нам надо будет очистить "полотно".

Public Sub ClearBuffer(dds As DirectDrawSurface4)
Dim Color As Long
Dim rc As RECT
Color = RGB(0, 0, 0) 'Задаем черный цвет
dds.GetSurfaceDesc ddsd 'Получить описание очищаемой поверхности
'Заполняем структуру RECT, так, чтобы она охватывала всю поверхность
With rc
.Top = 0
.Left = 0
.Right = ddsd.lWidth 'Вот зачем нам надо было описание поверхности
.Bottom = ddsd.lHeight 'Высота и ширина подгоняются под нее
End With
dds.BltColorFill rc, Color 'Заполняем черным цветом область, указанную в RECT
End Sub

С модулем закончили! Переходим к форме.

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

Private Sub Form_Load()
mdlDirect3DRM.InitFullscreen Me.hWnd, 640, 480, 16 'Инициализируем Direct3D
SetMyDefaults 'Делаем начальные установки
Running = True
Run 'Запускаем рендеринг
End Sub

Вот процедура Run, которая будет выполнять весь цикл рендеринга.

'Рендеринг
Private Sub Run()
  Do While Running = True
      DoEvents 'Обязательно вставляйте эту строку
      mdlDirect3DRM.ClearBuffer dds4Back 'Чистим задний буфер
      d3dRm.Tick 1 'Рендеринг кадра выполняется такой вот милой командой
      dds4Primary.Flip Nothing, DDFLIP_WAIT 'Показываем пользователю сцену
  Loop
  mdlDirect3DRM.DestroyEverything 'Уничтожаем все созданные объекты
  End 'Конец
End Sub

Чтобы нам не впасть в вечный цикл, надо предусмотреть выход из него. Будем выходить, когда нажмем Escape

Private Sub Form_KeyPress(KeyAscii As Integer)
Select Case KeyAscii
Case vbKeyEscape
Running = False
End Select
End Sub

Теперь, нам осталось написать только процедуру, которая создаст "мир".

Private Sub SetMyDefaults()
CreateMyObject 'Создаем объект. В виду "тупости" этого процесса, я вынес
'его в отдельную процедуру


Set World = d3dRm.CreateFrame(Scene) 'Создаем фрейм для объекта
World.AddVisual MeshBuilder 'Добавляем в "мир" визуальный объект
World.SetRotation Nothing, 0, 1, 0, 0.017 ' Придаем миру вращение в 10°
Camera.SetPosition Nothing, 0, 2, -20 'Устанавливаем камеру
End Sub

Сначала, мы создаем объект. В виду максимального упроцения, я создавал объект путем создания поверхностей по одной и дальнейшего их "слепления" в одно объединение. Из-за этого, процесс получился довольно объемный в написании, поэтому я вынем его в отдельную процедуру.

После того, как создали объект, надо его куда-то разместить. Так как мир у нас состоит из одной пирамиды, то создадим фрейм объекта World, который будет относиться к сцене. Затем, вы добавим к нему пирамиду, которую сконструировали в MeshBuilder. Придадим нашему Миру вращение (угол вращения вокруг указанных осей задается в радианах!) И напоследок, спозиционируем камеру по трем осям так, чтобы она смотрела на пирамиду.

Теперь создание пирамиды. Процесс очень нудный и до безобразия понятный. По одной мы создаем поверхности, придаем и цвет и прилепляем к общему объединению.

Private Sub CreateMyObject()
'Тупо создаем по поверхности, задаем ей цвет, и добавляем в объединение

Dim face1 As Direct3DRMFace2
Dim face2 As Direct3DRMFace2
Dim face3 As Direct3DRMFace2
Dim face4 As Direct3DRMFace2

Set face1 = d3dRm.CreateFace
Set face2 = d3dRm.CreateFace
Set face3 = d3dRm.CreateFace
Set face4 = d3dRm.CreateFace

face1.AddVertex -3, 0, -3
face1.AddVertex 0, 3, 0
face1.AddVertex 3, 0, -3
face1.SetColorRGB 1, 0.5, 1
MeshBuilder.AddFace face1

face2.AddVertex 3, 0, -3
face2.AddVertex 0, 3, 0
face2.AddVertex 3, 0, 3
face2.SetColorRGB 0.5, 1, 1
MeshBuilder.AddFace face2

face3.AddVertex 3, 0, 3
face3.AddVertex 0, 3, 0
face3.AddVertex -3, 0, 3
face3.SetColorRGB 1, 1, 0.5
MeshBuilder.AddFace face3

face4.AddVertex -3, 0, 3
face4.AddVertex 0, 3, 0
face4.AddVertex -3, 0, -3
face4.SetColorRGB 1, 1, 1
MeshBuilder.AddFace face4
End Sub

Ну вот вроде и все! Если я (или вы) ничего не забыл, то запустите программу, выберите ваш драйвер и пирамида должна крутиться, сверкая разноцветными сторонами.

Самое интересное, что я тут не добавил поверхность для нижней части пирамиды, поэтому если вы сможете поставить камеру так, чтобы она смотрела снизу на пирамиду, то вы увидите только пустоту. Это происходит потому, что поверхности прозрачны только с одной стороны. Если бы за пирамидой стояло что-то еще, вы бы увидели этот предмет сквозь пирамиду. Вот такая вот бага :)

Вы можете загрузить готовый проект

В следующей части мы загрузим объединение из файла .x и - радуйтесь! - натянем на него текстуру. Происходить все это будет в окне.

Приятного программирования, Antiloop


>>> Реклама <<<