Программа – трехмерный игровой движок на базе библиотек DirectX и PhysX

  • Вид работы:
    Дипломная (ВКР)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    2,96 Мб
  • Опубликовано:
    2013-09-02
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Программа – трехмерный игровой движок на базе библиотек DirectX и PhysX















ДИПЛОМНЫЙ ПРОЕКТ

Тема: «Программа - трехмерный игровой движок на базе библиотек DirectX и PhysX»

1. Введение

трехмерный игровой движок

В современном мире практически каждый человек, независимо от возраста, время от времени пользуется компьютером и современными средствами коммуникации и обработки информации. Огромное влияние на развитие вычислительной техники оказали видеоигры. Многие из нас проводят досуг за прохождением квеста или получением уровня в ролевой игре. Так же разработка игр - очень выгодная сфера деятельности, на фоне других областей программного обеспечения. Главная особенность игр в том, что их они никогда не потеряют свою популярность. Каждый день выходят новые игры, новые жанры и, чтобы удовлетворять запросам современных игр, в свою очередь, выходят новые технологические средства и технологии. Таким образом игры приносят прибыль не только своим непосредственным разработчикам, но и косвенно помогают развитию отрасли аппаратных средств.

Появились компьютерные игры практически вместе с первыми компьютерами. В то время игры представляли из себя двухмерные и монохромные (в силу двух цветности мониторов) программы. Со временем, когда компьютеры стали поддерживать графический вывод информации, разнообразные устройства ввода, игры стали интереснее и красочнее. С течением времени начали разрабатываться и технологии, позволяющие делать игры все более сложными и увлекательными. Одним из самых важных достижений можно назвать появление средств расчета трехмерного пространства, ведь до этого программистам приходилось вручную просчитывать все координаты.

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

Данная работа посвящена разработке игрового движка. Для написания кода используется язык C++, для вывода графики графический пакет DirectX9, а для взаимодействия объектов - физический движок PhysX.

Компьютерные игры имеют одну интересную особенность - любой среднестатистический играющий в них человек запросто может определить возраст игры, вплоть до года. Это связано с тем, что процесс разработки игр очень долгий и трудоемкий и включает в себя очень много аспектов. Обычно, на разработку игры уходит около года и за это время технологии и аппаратные решения уходят вперед. Чтобы ускорить процесс разработки игр вся разработка делегируется на группу людей, каждый из которой занимается областью, профессионалом которой он являются. В типичной группе энтузиастов обычно есть программисты, дизайнеры, сценаристы, звукорежиссеры и специалисты разработки 3D-моделей. Главная задача лежит на плечах программистов, которым необходимо разработать «движок» игры. «Движок» - это сердце игры. В зависимости от игры он включает в себя: получение вводимой пользователем информации, искусственный интеллект, просчет столкновений, визуализацию и много узконаправленных вещей, которые зависят напрямую от жанра игры, например, подсчет очков.

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

Главным свойством хорошего движка считается его гибкость. Чем большее количество игр можно написать с использованием данного движка, тем он лучше. Поэтому далеко не каждая игра имеет под собой свой собственный движок. В качестве примера можно привести движок SOURSE, разработанный компанией VALVE в 2005 году и являющийся актуальным до сих пор. Количество игр, написанное на нем, в первые годы существования перевалило за десяток. Каждый движок имеет определенную структуру и ее логичность является так же важным фактором. Простота подключения и освоения были козырем во все времена.

2. Общая часть

В этой части представлено описание начальной стадии разработки проекта, то есть подробное описание средств программирования и постановка задачи.

 

2.1 Цель разработки


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

Движок должен быть гибким, удобным и простым в освоении. Основной его задачей является визуализация, обработка ввода, просчет физики и работа с объектами в игровом мире.

 

2.2 Анализ средств программирования


Данный проект реализован на языке программирования С++ с использованием графической библиотеки DirectX и физического движка PhysX. Для написания шейдеров использовался язык программирования шейдеров высокого уровня - HLSL.

Библиотека DirectX представляет удобный контроль над многими аспектами разработки. Так, например, компонент DirectShow представляет уровень абстракции HAL, позволяющий избавиться от программирования графики для всех видеокарт. К сожалению, это крадет скорость работы программы. Архитектура DirectX основывается на технологии COM, которая представляет доступ к виртуальным интерфейсам через DLL-вызовы. Данный подход позволяет подключать DirectX практически к любой среде разработки, поддерживающей обращение к DLL.

Библиотека PhysX представляет тобой решение для просчета физики различных объектов и взаимодействие между ними. Все изменения игрового мира сначала просчитываются PhysX, после чего, по средствам движка, визуализируются через DirectX.

Для освоения DirectX существует много литературы. Для PhysX литературы нет, если не считать встроенные справочники по функциям и примеры.

В качестве среды разработки была выбрала Visual Studio 2012 от Microsoft. Причиной тому послужила простота работы со средой и личная неприязнь к другим средствам разработки. Так же Visual Studio предлагает удобные средства отладки программы, средства предварительной компиляции шейдеров на языке HLSL и удобны менеджер объектов, функций и классов - IntelliSense.

 

2.2.1 Обзор методов решения

В этом разделе рассматриваются альтернативные средства программирования и похожие программы.

Существует масса языков, с которыми может работать DirectX, благодаря тому, что в его основе лежит технология COM. Тоже самое относится и к PhysX. Связка данных библиотек с С++ является самой популярной, но это не говорит о том, что иные схемы невозможны.

Так, например, в качестве языка разработки можно использовать язык Delphi, а для вывода графики - OpenGL. Физику так же прекрасно можно обрабатывать по средством физического движка Bullet.

В данном случае был выбран именно С++, DirectX и PhysX. Причиной тому служило наибольше количество справочного материала по каждому из них.

В качестве ревизий библиотек были использованы не самые новые, но и не старые версии. Это решение связано с тем, что большинство новых библиотек менее стабильны и имеют меньше справочного материала.

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

 

2.2.2 Описание языка

Язык С++ является одним из самых популярных среди разработчиков программного обеспечения. Он является чрезвычайно мощным и используется для написания программ практически любого назначения. Язык объектно-ориентированный, позволяет работать с памятью, адресами и портами.

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

В качестве среды разработки было принято решение использовать Visual Studio 2012. Данный продукт позволяет создавать программы любой сложности на многих языках и представляет удобные средства отладки. Так же Microsoft предоставляет множество справочного материала по данному вопросу. Для работы программы не требуется мощный компьютер, но для корректной работы DirectX может потребоваться мощная видеокарта с поддержкой DirectX.

 

2.2.3 Общие сведения

Текст программы на С++ формируется с помощью букв, цифр и специальных символов.

В С++ программа имеет точку входа, которая обязательна для работы программы. Так же хорошим тоном считается вынесение заголовков функций в заголовочный файл (.h), а их реализацию в файл исходного кода (.срр).

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

Язык С++ поддерживает нативные типы данных, наряду с типами, которые может разрабатывать и сам программист. В качестве последних чаще всего выступают классы и структуры.

Основными для С++ являются следующие типы данных:

·        Int - целочисленный

·        Float - число с плавающей точкой

·        Char - символ

·        Double - число двойной точности

·        Long - длинный int

·        Short - короткий int

·        Bool - логический

Ниже представлены все ключевые слова в языке С++.

alignas (начиная с C++11) alignof (начиная с C++11) and and_eq asm auto(1) bitand bitor bool break case catch char char16_t(начиная с C++11) char32_t(начиная с C++11) class compl const constexpr(начиная с C++11) const_cast continue decltype(начиная с C++11) default(1) delete(1) do double dynamic_cast else

enum explicit export extern false float for friend goto if inline int long mutable namespace new noexcept(начиная с C++11) not not_eq nullptr (начиная с C++11) operator or or_eq private protected public register reinterpret_cast

return short signed sizeof static static_assert(начиная с C++11) static_cast struct switch template this thread_local(начиная с C++11) throw true try typedef typeid typename union unsigned using(1) virtual void volatile wchar_t while xor xor_eq


2.2.4 Способы структурирования

1 Процедуры и функции

Язык С++ поддерживает парадигму процедурного программирования. Так же он позволяет выносить заголовки функций в отдельное место. Для этого нужно указать тип возвращаемого функцией значения, ее название и типы входящих параметров.

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

Так же есть возможность написания процедур - для этого достаточно в качестве возвращаемого параметра указать тип void.

Модули

Язык С++ поддерживает модули. В качестве основного файла используется файл исходного кода с точкой входа в программу и заголовочный файл, содержащий классы, заголовки функций, структуры и т.п.

Файл проекта Visual Studio имеет расширение .vcxproj, а файл решения - .sln. Решение может содержать в себе несколько проектов.

 

.2.5 Дополнительные средства языка

Для данной программы не использовались никакие дополнительные средства разработки Visual Studio. В данном случае среда выступает как компилятор написанного кода, а все основные действия происходят за счет функций DirectX и PhysX.

Для того, чтобы C++ мог использовать DirectX и PhysX нужно подключить к проекту файлы заголовков и библиотеки, найти которые можно на сайте Microsoft.com и Nvidia.com. Существует две версии библиотек DirectX и PhysX - для конечного пользователя и для разработчика. Версии для разработчиков (SDK) представляют собой набор библиотек и заголовочных файлов, примеры и справочную информацию, в то время как версия для конечного пользователя представляет собой лишь библиотеки, которые необходимы для запуска приложения на компьютере.

DirectX

Компоненты DirectX 9

Direct3D

Содержит высокоуровневый интерфейс Retained Mode позволяющий легко выводить трехмерные графические объекты, и низкоуровневый интерфейс Immediate Mode, предоставляющий полный контроль над рендерингом. С девятой версии реализована новая технология шейдеров, позволяющая поднять уровень реалистичности изображения в новых играх.

Предназначен для отображения двумерной графики на экране монитора. Обеспечивает прямой доступ к видеоадаптеру, за счёт чего достигается быстрый вывод графики, по скорости на порядок превышающий GDI.

Создан для прямого доступа к аппаратной части звуковых плат, чтобы избавить воспроизведение звука от "тормозов", которые возникают при работе с Win32 API.

Эта компонента отвечает за любые устройства ввода, например, аналоговые или цифровые джойстики, рули и педали, ручку управления полётом, световой карандаш или Touch-Screen. Некоторые возможности компоненты можно реализовать средствами Win32 API, получив такой же быстрый доступ, но в некоторых областях DirectInput.

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

Предназначен для установки DirectX.

Используется в мультимедиа-технологиях - можно выполнить высококачественное воспроизведение или захват видео и звука.

Архитектура доступа базируется на технологии COM. Значит, для использования DirectX необходима среда программирования, поддерживающая обращение к функциям DLL из готовых программ и поддержка COM.

PhysX

PhysX представляет из себя физический движок, позволяющий в реальном времени эмулировать физические законы. Движок поддерживает такие объекты, как твердые тела, ткани и, в обозримом будущем, жидкости. Так же позволяет «присоединять» тела друг к другу не теряя при этом физических свойств. PhysX прекрасно справляется с деформацией объектов. Ткани, например, можно порвать, а твердые тела согнуть.

Изначально PhysX разрабатывался компанией Ageia и представлял из себя интегральную плату, которая использовалась наряду с видеокартой. В последствии разработка была куплена компанией NVidia и чип обработки физики был интегрирован в современные графические решения компании. В отличии от NVidia, AMD Radeon не поддерживает аппаратного ускорения физики. Так же PhysX поддерживается многими игровыми платформами, такими как PS3 и XBOX.

В данном продукте PhysX используется для обработки только твердых тел. Изначально все объекты сцены не имеют физических свойств, но их можно включить специальной командой.

3. Специальная часть


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

 

3.1 Постановка задачи


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

 

3.1.1 Назначение задачи

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

Цель задачи - создание удобного, гибкого и универсального «движка» на языке программирования Delphi, который будет использовать DirectX.

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

Инициализация графики. В DirectX этот процесс достаточно объемный и трудоемкий. Будет удобнее вынести его в отдельную функцию. Теперь необходимо определиться, какой режим будет поддерживать эта программа. Дело в том, что инициализация оконного и полноэкранного режима происходит про разному. Большинство компьютерных игр поддерживают только полноэкранный режим, с другой стороны, существует не мало игровых программ, работающих и в оконном режиме. В целях универсальности эта программа должна поддерживать как оконный так и полноэкранный режим.

Загрузка сетки из Х-файла. В трехмерном пространстве существуют всего три вида примитивов - это точка, отрезок и треугольник. Все остальные примитивы можно построить с помощью этих трех составляющих. Основным примитивом в Direct3D, конечно, является треугольник. Почему в качестве основы используется именно треугольник, и нет более сложных фигур? Дело в том, что сами видеоускорители при формировании графики оперируют именно треугольниками, поэтому логичнее будет, если программа будет «разговаривать» с устройством на додном языке и оперировать одними и теми же данными.

Для построения одного треугольника нужно три точки, каждая из которых должна иметь, как минимум 3 параметра - это координаты в трехмерном пространстве (x,y,z) и цвет вершины. Для создания объемной фигуры понадобится создание буфера индексов и буфера вершин. Каждую точку, определяющую вершину треугольника нужно четко расчитать. Из всего этого следует вывод, чтобы построить фигуру сложнее куба, нужно потратить не один час времени. И как, например, нарисовать фигуру человека таким образом, да еще и правильно натянуть на расчитанную сетку текстуру. Для решение проблемы корпорация Microsoft разработала формат Х. В файле с расширением «Х» может храниться информация о сетке, текстуре, скелете и даже анимации. DirectX имеет ряд очень удобных функций для работы с Х-файлами и это очень упрощает программирование графики. Чтобы создать сетку и сохранить ее в файле с расширением «х», нужно, чтобы этот трехмерный редактор поддерживал экспорт в этот формат. Для программы 3D Studio Max, Microsoft выпустил специальное дополнение, с помощью которого созданые в 3D Studio Max объекты можно импортировать в формат «х».

Итак, этот проект должен загружать сетки и, если есть, текстуры из Х-файла. Кроме того Необходима функция для натягивания текстуры из bmp файла, на объект.

Расчеты положения игровых объектов. Игровым объектом является: источник освещения, камера, сетка (загруженная из Х-файла или описаная в ручную). Расчет положения объекта производится с помощью векторов (в случае с источником света) и матриц (с помощью матриц расчитывается положение камеры и всего игрового мира). Что казается сетки, то тут все достаточно просто - есть матрица положения, с помощью которой можно деформировать и перемещать объект. С камерой все несколько сложнее. Камера в игровой программе - это персонаж, который должен перемещаться по игровому пространству по команде игрока (пользователя). Игровой «движок» должен расчитывать положение и направление камеры в игровом пространстве оперируя матрицами.

Диолог с пользователем. Все выше перечисленые действия можно реализовать с помощью одного только модуля Direct3D. Для диолога с пользователем понадобится модуль DirectInput, который будет производить чтение с устройств ввода. Чтобы узнать какое действие произвел пользователь можно было бы воспользоваться WinAPI функциями. Но WinAPI работает через Windows, и это значительно снижает производительность. Компьютерные игры - это та область программирования, где нет лишних ресурсов и программы должны работать максимально быстро. К тому же есть еще один нюанс в работе WinAPI, если зажать и не отпускать одну клавишу (например, при движении игрока прямо на некоторое расстояние) Windows выдаст сообщение о повторном нажатии одной и той же клавиши, что не допустимо в игре. DirectInput работает с устройством в обход Windows и достигает максимальной скорости считывания информации.

Программа «движка» должна инициализировать основные устройства ввода (клавиатура, мышь), считывать из них информацию, и, в соответствии с введенными пользователем данными, производить определенные действия (например, при нажатии клавиши «left» сместить камеру влево).

 

3.1.2 Требование к программе

Требования к функциональным характеристикам

Существует множество подобных программ и все они ориентированы на разные приоритеты. Одни работают очень быстро и занимают минимальное место в памяти компьютера, но код такой программы сложен в понимании. Другие работают верно и четко, но скорость выполнения оставляет желать лучшего (проверки на ошибки отнимают достаточно процессорного времени, чтобы программа начала зависать даже на самом современном компьютере). Третии легко расширяемы, универсальны и понятны, но занимают слишком много памяти. Найти идеальное сочитание всех этих качеств подсилу не каждому опытному программисту.

Основные качества, которыми должен обладать этот проект:

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

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

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

Итак, этот проект должен быть расширяемым, универсальным и простым в понимании. Несмотря на то, что именно эти качества являются основными, нельзя пренебрегать ресурсами компьютера. Нельзя забывать, что слишком медленный «движок» вряд ли кому-нибудь понадобится.

Требования к аппаратным и программным средствам

На компьютере должна быть установлена операционная система Windows и DirectX 9 последней версии.

Для использования программы необходимы заголовочные файлы для DirectX 9 и среда разработки Delphi 7.

Требования к аппаратным средствам

На компьютере должен работать Delphi 7. О требованиях к аппаратным ресурсам компьютера для Delphi 7 уже было сказано в разделе «описание языка».

Проект занимает 10 Mb свободного места на диске. Плюс к этому 4 Mb понадобятся для размещения «заголовочных файлов».

 

3.2 Описание алгоритма (блок-схема)


В этом разделе представлена краткая блок-схема программы. Подробное описание ее представлено в следующем разделе.

 

3.2.1 Описание блок-схемы

При запуске приложения происходят следующие действия:

1.   Построение окна - задание его свойств и создание с помощью функции WinAPI.

2.         Инициализация DirectX - инициализация графики, проектирование окна, создание устройства для работы с Direct3D, запуск конструктора движка.

3.         Бесконечный цикл обработки сообщений - сообщения в цикле обрабатываются с помощью WinAPI функций. Если сообщений нет, то происходит вызов функции движка игры, в котором происходит формирование сцены и обработка данных, полученных с устройств ввода.

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

 

3.2.2 Краткая блок-схема файла проекта


 

3.3 Описание программы


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

 

3.3.1 Описание структуры программы

Программа состоит из нескольких файлов: файл проекта, 3 модуля, Х-файлы и файлы текстуры для тестирования программы. Файл проекта называется Game.dpr, это скелет игрового приложения. В нем происходит инициализация, обработка событий, вызов функций «движка». Модули - dxfunc, GraphEngine, DXGObject. Файл проекта и эти модули связаны между собой и используют функции друг друга. Ниже представлена схема взаимодействия модулей.

3.3.2 Организация данных в программе

Движок - это основная часть программы, все данные проходят через него. Но начать стоит с описания главной программы.

В главной программе (проекте) задаются параметры окна, обрабатываются сообщения windows, происходит вызов функций движка и функции инициализации из соответствующего модуля.

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

wc : TWndClassEx; Класс окна.

pWnd : HWND; Окно.

pMsg : TMsg; Сообщения Windows. С помощью этой переменной программа получает и обрабатывает сообщения.

pD3D: IDirect3D9=nil; Переменная для работы с Direct3D. Она предназначена для хранения данных о главном устройстве Direct3D.

pD3DDevice: IDirect3DDevice9=nil; Переменная для работы с Direct3D. Она предназначена для хранения данных о устройстве Direct3D.

ge:CGraphEngine; Переменная движка игры. Эта переменная служит для доступа к функциям класса, созданного в модуле движка игры (GraphEngine).

После создания окна, в главной программе происходит вызов функции инициализации, в которой происходит инициализация и вызов конструктора движка. Функции инициализации DX3DInit из модуля dxfunc передаются переменные для работы с Direct3D, созданное окно и разрешение. Результат работы процедуры инициализации - это число типа Boolean (логический тип) True или false.

DX3DInit(pD3D, pD3DDevice, pWnd, 800, 600, b)

Если процедура инициализации вернула значение False, значит, инициализация не удалась. Далее производится вызов конструктора движка

ge:=CGraphEngine.CGraphEngine(pD3DDevice, pWnd);

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

Инициализация завершена. Далее в главной программе с помощью процедуры ShowWindow производится показ окна. Далее обновляем окно (UpdateWindow(pWnd);). Это необходимо, так как просле инициализации Direct3D происходят изменения параметров окна, и если не обновить окно здесь, то в процессе выполнения программы возникнут проблемы с отображением.

Далее идет запуск бесконечного цикла обработки сообщений.

while (true) do

begin(PeekMessage(pmsg, 0, 0, 0, PM_REMOVE)) then

//если есть сообщение, то обрабатываем его

begin

TranslateMessage(pmsg);

DispatchMessage(pmsg);

if (pmsg.message = WM_QUIT) then exit; //если нажата клавиша выхода, то

//завершаем работу программы

end

else

GraphEngine(); //вызываем функцию движка

end;

В цикле производится проверка, есть ли сообщение, если есть, то оно обрабатывается с помощью WinAPI функций. Если сообщений нет, то вызываем функцию движка.

В функции движка происходит отображение сцены. Сначала, с помощью функции BeginScene() интерфейса IDirect3DDevice9, начинаем формирование сцены. Далее, если процедура прошла успешно, идет очистка экрана и формирование сцены с помощью процедуры RenderScene(), которая находится в модуле движка GraphEngine.(SUCCEEDED(pD3DDevice.BeginScene())) then //Если процедура BeginScene была выполнена, то вызываем движок

begin

//очистка экранаDDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0, 0);

ge.RenderScene(); //формирование сцены

pD3DDevice.EndScene(); //конец отображения сцены;DDevice.Present(nil, nil, 0, nil); //Функция для отображения сцены, без нее сцена

//не сформируется;

В процедуре RenderScene() происходят самые основные действия.

procedure CGraphEngine.RenderScene();i:integer;

begin

//если функция KeyControl вернула false, значит дальнейшее выполнение программы

//не имеет смысла (программа не реагирует на устройства ввода)KeyControl=false then exit; //выходим из программы если функция KeyControl вернула false

//Отображаем сцену.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

//Очищаем переменную устройства Direct3D.Clear(0, nil, D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 1.0, 0);

//Отображаем объекты на экранеi:=0 to CURRENT_OBJECTS_NUM-1 do

 objects[i].Render(0);

end;

Вызов процедуры KeyControl, которая возвращает логический тип. Если эта процедура вернула false, значит чтение с устройтв ввода не удалась, а это значит, что дальнейшее продолжение работы программы не имеет смысла, тогда функция завершает работу программы (Exit). Далее отображение сцены. После отображения необходимо очистить переменную устройства Direct3D. И последнее - отображения объектов на экране с помощью процедуры Render из модуля движка объектов DXGObject.

function CDXGObject.Render(ef_index:DWORD):boolean; i: integer;

begin

//присваеваем результату работы функции значение false

Result:= false;();

// отображение объектаi:=0 to dwNumMaterials-1 do

pDevice.SetMaterial(pMeshMaterials[i]); //устанавливаем материал

if pMeshTextures[i]<>nil then //устанавливаем текстуру, если она есть

pDevice.SetTexture(0, pMeshTextures[i]);.DrawSubset(i); //Рисуем сетку;

//функция выполнира работу успешно и присваеваем результату значение true

result:=true;

end;

В качестве параметра функция принемает номер объекта (игровых объектов может быть очень много, это локации, персонажи и прочий антураж). Здесь происходит следующее: устанавливаем позицию объекта, в которой он будет отображен на экране с помощью процедуры Positioning();. Далее в цикле (где dwNumMaterials - количество объектов) происходит отображение объекта, то есть установка материала, текстуры и отображение сетки. Если все эти действия выполнены успешно, то результату функции назначается True, который эта функция возвращает движку игры.

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

В теле функции WindowProc первым делом идет конструкция case, параметром которой является переменная сообщений msg; если она равна WM_CREATE, то программа ничего не делает; если msg равна WM_DESTROY, значит программа освобождает захваченные ресурсы. Иначе результату функции присвоить результат работы функции DefWindowProc(wnd,msg,wparam,lparam).

 

3.3.3 Функции модулей

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

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

Функции и переменные модуля «движка» игры - GraphEngine

Константы:

NUM_OBJECTS = 20; Максимальное количество игровых объектов. Эта переменная используется в некоторых циклах, в качестве параметра количества раз повторения цикла. В дальнейшей разработке программы ее можно легко поменять.

CURRENT_OBJECTS_NUM = 2; Реальное количество объектов. Эта константа описана для того, чтобы было легче увеличивать количество объектов. Она упрощает расширение программы.

Глобальные переменные:

pDevice:IDirect3DDevice9; Устройтво Direct3D

objects:array [0..NUM_OBJECTS] of CDXGObject; Массив игровых объектов. Эта переменная, типа CDXGObject. CDXGObject - это класс, содержащийся в модуле движка объекта DXGObject. В этом массиве содержатся данные о всех игровых объектах.

lpDI8: IDirectInput8 = nil; Главное устройтво DirectInput

lpDIKeyboard: IDirectInputDevice8 = nil; Клавиатура.: IDirectInputDevice8 = nil; Мышь., xrMat, yrMat, mvMat:D3DMATRIX; Матрицы для преобразования. Эти переменные представляют собой матрыцы, размером 4х4 и используются при задании положения камеры в виртуальном мире.

pos, vecLR, vecFB, up: TD3DXVECTOR3; Векторы направления осмотра. Эти переменные представляют собой трехмерные векторы, которые определяются 3 вещественными числами.

Функции:

1.         constructor CGraphEngine(pD3DDevice:IDirect3DDevice9; hWnd: HWND); В качестве первого параметра функции конструктора передается указатель на устройство Direct3D, второй параметр - окно программы. В разделе переменных функции имеется переменная w типа D3DMATRIX, она будет использоваться для задания мировой матрицы. В теле функции переменной pDevice назначается устройство, созданное в главной программе. Далее задается начальное значение векторов направления, инициализируются матрицы, корректируется значение матрицы w, мировой матрице назначается значение w. Далее вызываются функции инициализации: функция инициализации объекта - InitGraphObjects() и функция инициализации устройств ввода - InitDirectInput(hWnd).

2.         function InitObject(typ:string;index:integer):boolean; В качестве первого параметра функция принемает строку, в которой содержится название объекта, второй параметр - это индекс объекта (его порядковый номер). Первым делом в этой функции происходит вызов конструктора движка объекта, которому передается указатель на устройство Direct3D. Далее идет вызов функции LoadMeshFromFile из модуля DXGObject. В зависимости от имени объекта в эту функцию передаются имена разных файлов. Первый - сетка, второй - текстура. И Завершает процедуру присвоение ей результата true, который она возвращает. Исходный код см. в приложениях.

3.         procedure InitGraphObjects(); Эта процедура вызывается в конструкторе данного модуля. Переменные процедуры: pos, pos1, rot: D3DMATRIX; - матрицы позиции объектов (rot - используется, как вспомогательная матрица при повороте объекта); lp, lp1, lp2: TD3DXVECTOR3; - вектора направления освещения. Сначала идет вызов InitObject, с помощью которой объекты загружаются в программу. Далее задается матрица позиции объекта. С помощью этой матрицы задается размеры, поворот и положение объекта в трехмерном мире. С помощью процедуры SetWorldPos эта матрица передается в движок объекта и назначается ему. После инициализации и установки позиции объекта в этом коде идут две функции преобразования матриц. Они индивидуальны, и написаны конкретно для этой ситуации - необходимо повернуть объект «человека», поскольку по умолчанию сетка человека располагается горизонтально. Эти функции поворачивают объект «человека» так, чтобы он находился в вертикальном положении. И только после этого вызывается процедура SetWorldPos для позиционирования «человека». Последним действием процедуры InitGraphObjects устанавливаются источники освещения. Сначала определяются вектора направления освещения. Их может быть несколько. Для установки источника света вызывается процедура SetLightPos, которой передаются два параметра - вектор направления освещения и индекс лампочки. Индексы лампочки должны быть разные, так как если указать одинаковые индексы, вторая устанавливаемая лампочка уничтожит первую.

4.         procedure RenderScene(); Эта процедура находится в разделе public, так как должна вызваться в главной программе. Она формирует и отображает сцену. Первым делом в этой процедуре вызываем функцию KeyControl, которая обрабатывает данные, полученные с устройств ввода. Если эта функция возвращает False, значит устройства ввода не готовы для передачи данных, в таком случае продолжать работу программы не имеет смысла и производится выход из программы, с помощью процедуры exit. Далее идет отображение сцены и задаются параметры отображения с помощью функций интерфейса IDirect3DDevice9. Очищается переменная этого интерфейса и отображаются объекты на экране. Для этого организуется цикл от 0 до количества объектов - 1, в котором вызывается процедура модуля движка объекта - Render, которая отображает объекты (подробнее о ней можно узнать в описании следующего модуля). На этом процедура RenderScene заканчивается.

5.         procedure InitDirectInput( hWnd: HWND ); Эта процедура вызывается на этапе инициализации. В качестве параметра ей передается окно программы, для того, чтобы «прикрепить» к нему устройства ввода. В разделе «var» (описание переменных) этой процедуры одна переменная - dipropdw: TDIPROPDWORD; - структура для задания характеристик мыши. Сначала создается главный объект DirectInput с помощью функции DirectInput8Create. Далее, с помощью метода CreateDevice и _AddRef() интерфейса IDirectInputDevice8 создается объект для работы с клавиатурой. Устанавливается предопределённый формат для "простой клавиатуры" с помощью метода SetDataFormat интерфейса IDirectInputDevice8. В большинстве случаев можно удовлетвориться и установками, заданными в структуре c_dfDIKeyboard по умолчанию, но в особых случаях нужно заполнить её самому. Устанавливаем уровень кооперации (уровень взаимодействия клавиатуры и окна программы) с помощью метода SetCooperativeLevel интерфейса IDirectInputDevice8, В качестве первого параметра этому методу передается окно программы, далее флаги (подробности о флагах можно посмотреть в DirectX SDK, в модуле DirectInput). В завершении всего выше изложенного, производится захват клавиатуры с помощью функции интерфейса IDirectInputDevice8 - Acquire. Аналогичные действия производятся для подключение мыши к программе.

6.         procedure ReleaseDirectInput(); Эта процедура производит освобождение захваченных DirectInput ресурсов. Уничтожение объектов должно производиться в порядке, обратном их созданию. Сначала производится освобождение устройства клавиатуры и мыши, затем уничтожение главного объекта DirectInput. Эта функция достаточно проста и понятна даже по названиям функций, используемых в ней.

7.         function KeyControl():boolean; С помощью этой функции программа читает данные с устройств ввода и производит необходимые действия в зависимости от полученных данных. В функции так же происходит установка матриц, векторов направления и включение освещения. Код функции можно посмотреть в приложениях. Переменные функции - View:D3DMATRIX; - матрица положения камеры, bKeyBuffer: array [0..255] of Byte; - буфер-массив клавиш клавиатуры, ms: TDIMOUSESTATE; - переменная для данных, полученных от мыши. Что здесь происходит - сначала вызывается метод GetDeviceState интерфейса IDirectInputDevice8, который производит опрос состояния клавиш и записывает данные в буфер-массив. Далее, в зависимости от того, какая нажата клавиша производим перемещение камеры с помощью функций MoveCamera и RotateCamera о которых позже. Проверка нажатия клавиши производится с помощью 16-ричного числа 080 (маска нажатой клавиши). Далее таким же способом производится опрос мыши. Считанные данные записываются в специальный буфер-массив. По ним можно определить нажатия клавиш и изменения по двум осям - х и у. В зависимости от смещения по осям передаются данные в функцию RotateCamera. В этой программе не задействованы клавиши мыши, есть только проверка на их нажатие, это сделано для того, чтобы в будущем программист мог легко задействовать эти клавиши в своей игре. Далее в этой функции задаются вектора направления и преобразуются матрицы положения камеры. О том как происходит этот процесс см. в следующем пункте «технико-математическое описание задачи».

8.         Procedure MoveCamera(fOffset:real; vecModifier:TD3DXVECTOR3); Эта процедура реализует перемещение камеры по виртуальному миру. Подробное описание математических действий описано в пункте «Технико-математическое описание задачи». Первый параметр - значение, на которое нужно передвинуть камеру, второй параметр - матрица положения камеры. Итак, сначала заполняются вспомогательные переменные, затем осуществляется преобразование вектора позиции камеры, и последний шаг - коррекция матрицы позиции камеры, с учетом изменений вектора.

9.         procedure RotateCamera(fOffset:real; Axis:integer); Эта процедура похожа на предыдущую. В качестве первого параметра она получает значение, на которое нужно повернуть камеру, в качестве второго - ось, вокруг которой нужно повернуть камеру. Если Axis = 0 поворот производится вокруг оси у, если 1 то по х. Подробное описание математических действий описано в пункте «Технико-математическое описание задачи».

Функции и переменные модуля «движка» объекта - DXGObject

Константы:

MAX_OBJECT_EFFECTS = 10; - Максимальное количество эффектов (используется при установки освещения).

Глобальные переменные:

Все глобальные переменные движка объекта описаны в классе - СDXGObject.

pDevice:IDirect3DDevice9; - указатель на устройство Direct3D;

dwNumMaterials:DWord; - В этой переменной хранится количество загруженных элементов из Х-файла (объект может состоять из нескольких сеток).

pMesh:ID3DXMesh; - Сетка Mesh.

pMeshTextures:PAIDirect3DTexture9; - Текстура объекта.

pMeshMaterials:PAD3DMATERIAL9; - Материал объекта.:array [0..MAX_OBJECT_EFFECTS] of IDirect3DTexture9; - Массив текстур.:array [0..MAX_OBJECT_EFFECTS] of D3DLIGHT9; - Массив источников освещения.: D3DMATRIX; - Мировая матрица.

Функции:

1.   constructor CDXGObject(pD3DDevice:IDirect3DDevice9); Конструктор. В качестве параметра конструктору класса объекта передается образец устройства Direct3D. Далее производится получение образца устройства Direct3D и инициализация мировой матрицы.

2.         procedure LoadMeshFromFile(filename:string; texturename:string); Процедура Загрузки Х-файла. Эта процедура получает в качестве первого параметра строку, содержащую путь к файлу сетки объекта, а в качестве второго параметра - строку, содержащую путь к файлу текстуры. В теле процедуры производится только одно действие - переменной dwNumMaterials назначается результат работы функции LoadMesh, которая находится в модуле dxfunc. Эта функция производит чтение информации из Х-файла.

3.         procedure LoadTexture(filename:PAnsiChar; index:integer); - Процедура Загрузки текстуры. Эта процедура в качестве первого параметра получает имя файла, содержащего текстуру объекта, а в качестве второго параметра индекс текстуры (у одного объекта может быть несколько текстур). В теле этой процедуры происходит вызов процедуры Direct3D - D3DXCreateTextureFromFileEx, которой передаются: устройство Direct3D, путь к файлу с текстурой, флаги и переменная текстуры (элемент массива текстур).

4.         function GetWorldPos():D3DMATRIX ; - Функция получения позиции мира (мировой матрици). Эта процедура получает мировую матрицу.

5.         procedure SetWorldPos(matrix:D3DMATRIX); - Процедура определения положения объекта. Эта процедура определяет мировую матрицу.

6.         procedure SetLightPos(Pos:TD3DXVECTOR3; index:integer); - Процедура установки источника освещения. В качестве первого параметра эта процедура получает трехмерный вектор, который определяет направление источника света. В качестве второго параметра процедуре передается индекс лампочки. В теле процедуры производятся следующие действия: выделение памяти под структуру типа D3DLIGHT9. Отдельно заполнение этой структуры: первый параметр - _Type - тип освещения, в данном случае он равен константе D3DLIGHT_DIRECTIONAL, что означает рассеянный свет. Следующий параметр - Direction, с помощью него задается направление освещения (равен Pos). Следующие 4 параметра определяют цвет освещения (в данном случае все они равны 1, а значит освещение белое). После заполнения структуры необходимо установить лампочку, это производится с помощью функции SetLight интерфейса IDirect3DDevice9. Включить лампочку - LightEnable.

7.         procedure Positioning(); - Процедура установки позиции объекта. В теле этой процедуры производится установка матрицы позиции объекта, с помощью функции SetTransform интерфейса IDirect3DDevice9. Первый параметр этой функции - это константа, определяющая тип матрици (в данном случае используется D3DTS_WORLD, которая означает, что это мировая матрица). В качестве второго параметра производится вызов функции GetWorldPos (ее описание см. выше).

8.         function Render(ef_index:DWORD):boolean; - Функция отображения объекта. В качестве параметра функция получает номер (индекс) объекта. В теле функции первым делом вызывается функция Positioning. После установки позиции объекта запускается цикл for от 0 до количества загруженных сеток - 1 (минус один). В цикле: с помощью функции SetMaterial интерфейса IDirect3DDevice9 устанавливаем материал, далее устанавливается текстура, если она есть, с помощью функции SetTexture интерфейса IDirect3DDevice9; следующий шаг цикла - отображение сетки с помощью функции DrawSubset интерфейса ID3DXMesh; конец цикла. Далее присваиваем результату работы программы True.

3.3.3.3 Функции и переменные модуля инициализации графики и загрузки Х-файлов - dxfunc

Типы:

PAIDirect3DTexture9 - Представляет собой массив неопределенной размерности, состоящий из указателей на тип IDirect3DTexture9. Этот тип необходим для работы с текстурами.

PAD3DMATERIAL9 - Представляет собой массив неопределенной размерности, состоящий из указателей на тип TD3DMATERIAL9. Этот тип необходим для работы с материалом.

PAD3DXMATERIAL - Представляет собой массив неопределенной размерности, состоящий из указателей на тип TD3DXMATERIAL. Этот тип необходим для работы с материалом.

Функции:

.function DX3DInit(var ppiD3D9: IDirect3D9;

var ppiD3DDevice9: IDirect3DDevice9;: THandle;, iHeight: Integer;bFullScreen: Boolean):Boolean;

Параметры функции DXDInit:

ppiD3D9 типа IDirect3D9 - для хранения интерфейса Direct3D 9-й версии;DDevice9 типа IDirect3DDevice9 - для хранения интерфейса устройства IDirect3DDevice 9-й версии;

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

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

Первое, что делается внутри функции - создается экземпляр интерфейса IDirect3D9 с помощью функции DirectЗDCreate9 и сохраняется результат в переменной ppiDЗD9. Этот интерфейс используется для создания объектов Direct3D и для настройки окружения. В качестве параметра необходимо передать константу D3D_SDK_VERSION(). Эта константа описана в заголовочном файле Direct3D9, и ее значение увеличивается в каждой версии. По значению константы DirectX определяет, является ли заголовочный файл корректным. Если значение не будет совпадать, то функция вернет ошибку. Проблему решит перекомпиляция программы с корректным заголовочным файлом.

Далее заполняется структура типа DЗDPRESNT_PARAMETERS, которая определяет параметры представления. Прежде чем использовать структуру, необходимо заполнить ее нулевыми значениями, чтобы незаполненные поля использовали значение по умолчанию. Подробное описание этой структуры см. в разделе «Общая часть» / «дополнительные средства языка» / «DirectX».

Далее следует запрос на отображение в полноэкранном режиме. Если переменная iRes равна IDYES, значит отображать следует в полноэкранном режиме. Тогда задаются параметры полноэкранного режима в структуре типа DЗDPRESNT_PARAMETERS. Иначе (если iRes не равно IDYES) задаются параметры оконного режима.

Далее переменной hRes присваивается значение работы функции CreateDeviсe интерфейса IDirect3D9. Следующим действием в процедуре инициализации необходимо установить перспективу и отключить освещение.

2.function LoadMesh(filename:String;DDevice9:IDirect3DDevice9;ppMesh:ID3DXMesh;pMeshTextures: PAIDirect3DTexture9;: String;pMeshMaterials: PAD3DMATERIAL9):DWORD;

Параметры функции:

filename:String; - Имя загружаемого Х-файла;

ppiD3DDevice9:IDirect3DDevice9; - указатель на интерфейс IDirect3DDevice9;

var ppMesh:ID3DXMesh; - переменная типа ID3DXMesh.Этот интерфейс используется для хранения и работы mesh-поверхностями;

var pMeshTextures: PAIDirect3DTexture9; - указатель на массив интерфейса IDirect3DTexture9. Этот интерфейс используется для хранения текстур;

texturefilename: String; - указатель на имя файла текстуры по умолчанию. Имена текстур могут храниться прямо в Х-файле, но если там нет текстуры, то используется файл из этого параметра. Если уже и здесь текстуры не будет, тогда объект не обтягивается текстурой;

var pMeshMaterials: PAD3DMATERIAL9:DWORD; - указатель на массив структур типа D3DMATERIAL9, который определяет материал объекта.

Переменные функции

pD3DXMtrlBuffer: ID3DXBUFFER; - это буфер, в котором будут храниться используемые в файле материалы;

dwNumMaterials: DWORD; - Необходима для хранения количества загруженных сеток.

d3dxMaterials: PAD3DXMATERIAL; - материал.

Первая процедура, которая вызывается в теле этой функции - это D3DXLoadMeshFromX(PAnsiChar(filename), D3DXMESH_SYSTEMMEM, ppiD3DDevice9, nil, @pD3DXMtrlBuffer, nil, @dwNumMaterials, ppMesh); в качестве параметров в эту процедуру передается имя загружаемого файла, опции, которые должны использоваться при загрузке (опций достаточно много, но в данном случае используется D3DXMESH_SYSTEMMEM, которая означает, что загрузка происходит в системную память); следующий параметр - указатель на устройство Direct3D; далее указатель на буфер для смежных граней; указатель на буфер для материалов; указатель на буфер для экземпляров эффектов; через предпоследний параметр будет получено количество загруженных материалов; и последний параметр - это непосредственно указатель на объект сетки.

Загрузив данные, нужно разобраться с материалами и при необходимости загрузить текстуры, которые сейчас находятся в буфере pD3DXMtrlBuffer. Сначала определяется указатель на данные буфера с помощью метода pD3DXMtrlBuffer.GetBufferPointer(); После выполнения этого метода программе известно количество материалов, поэтому далее идет инициализация массивов для их хранения. Массивы динамические и их инициализация проходит с помощью процедуры GetMem. Первый массив выделяет память для хранения материалов, второй для хранения такого же количества текстур. Далее идет запуск цикла, в котором будут перебираться все материалы от 0 до значения dwNumMaterials. В цикле сначала капируется материал в i-й элемент массива pMeshMaterials. Затем нужно загрузить картинку, используемуюв качестве текстуры. Для загрузки картинки применяется функция D3DXCreateTextureFromFile.

В качестве результата функция возвращает количество загруженных объектов.

.function LoadTexture(text_buf:PByteArray;iWidth, iHeight:Integer; filename:String):Boolean; - С помощью этой функции можно загрузить текстуру и натянуть ее на объект, созданный программно. Краткое содержание этой функции: чтение файла, проверка заголовка (является ли файл bmp, то есть точечным рисунком) и если является, загружаем его. Эта функция для достаточно индивидуальных случаев и в данном проекте не применяется. Однако ее существование очень полезно, поскольку функция может очень пригодиться для создания какой-нибудь картинки, возможно для заставки или игрового меню. Ведь слишком неоправданны были бы потери процессорного времени при загрузки сетки, состоящей из двух треугольников, из Х-файла.

 

3.3.4 Технико-математическое описание задачи

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

Матрица положения объекта и направление источника света должна задаваться программистом в ручную. Для демонстрации работы «движка» необходимо расчитать положение двух объектов, которые будут загружаться в программу. Это человек и комната. Человек должен находиться в центре комнаты, Будет проще использовать для расчета положение человека и комнаты единичную матрицу, тогда получится, что центр человека и комнаты находится в точке x=0, y=0, z=0. Но по скольку сетки получились не пропорциональными по отношению друг к другу, немного подредактируем матрицу, чтобы изменить масштаб.

Матрица положения человека:Матрица положения комнаты:

.10000.1000

.10000.100

.10000.10

Источники освещения расположены случайным образом.

Расчет положения камеры. При нажатии определенных клавиш камера должна перемещаться по миру в разные стороны. Расчет движения производится в отдельной функции. Функции передается два значения - смещение, на которое нужно переместиться, и вектор, определяющий движение. Расчет движения происходит следующим образом - рассчитывается новое положение вектора, смещая его на указанное число, затем задается матрица перемещения на основе рассчитанного вектора, которая перемножается с матрицей движения. В программе это выглядит следующим образом:DXVec3Subtract(vect, vecModifier, pos );

//рассчитываем положение вектора (по оси y перемещение происходить не будет).x:=vect.x*fOffset; vect.y:=0; vect.z:=vect.z*fOffset;

// расчитываем матрицу положенияDXMatrixTranslation(tView, vect.x, vect.y, vect.z);DXMatrixMultiply(mvMat, mvMat, tView);

Если произошло движение мышью, то необходимо повернуть камеру. Для этого тоже создана отдельная функция. В качестве параметров функция получает угол, на который нужно повернуться, ось, вдоль которой нужно вертеться. Если ось равна 0, то поворачивается вдоль оси Y (по горизонтали), если 1, то вдоль оси Х.

Затем необходимо задать вектора и определить положение матрицы.

Сначала задаем вектор позиции pos, равный (0,0,0)

Для движения вперед/назад определяем вектор vecFB, равный (0, 0, -10). Из его значения видно, что движение происходит вдоль оси Z, которая направлена в глубь экрана.

Для движения влево/вправо определяем вектор vecLR, равный (10,0,0). Этот вектор направлен вдоль оси Х. Ось Х всегда направлена вправо, поэтому тут необходимо просто указать в этой составляющей положительное число. В данном случае выбрано число10.

Для расчетов используется еще один вектор - up, который равен (0,10,0). Этот вектор определяет, куда смотрит макушка персонажа. Поскольку персонаж не наклоняется, вектор направлен вдоль оси Y, ровно вверх.

После того как векторы определены перемножаем матрицы с помощью специальной функции DirectX

//Перемножаем полученные, в результате движения, матрицы

View:=worldMat;DXMatrixMultiply(View, xrMat, yrMat);3DXMatrixMultiply(View, View, mvMat);

Теперь необходимо трансформировать координаты с учетом векторов, с помощью функции DirectX - D3DXVec3TransformCoord, которой передаем 3 параметра - вектор, вектор и матрица положения камеры.

D3DXVec3TransformCoord(pos, pos, View);DXVec3TransformCoord(vecFB, vecFB, View);DXVec3TransformCoord(vecLR, vecLR, View);DXVec3TransformNormal(up, up, View);

И последнее действие - устанавливаем положение камеры -

D3DXMatrixLookAtLH(View, pos, vecFB, up);.SetTransform(D3DTS_VIEW, View);

 

3.4 Инструкция пользователя


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

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

 

3.4.1 Как работать с «движком»

Для начала необходимо посмотреть на программный код всех модулей, который можно увидеть в разделе «Приложения». Основные изменения при программировании собственных проектов, нужны в функциях «движка» InitObject, InitGraphObject и KeyControl.

Загрузка пользовательского объекта

Для того, чтобы добавить загрузку еще одного объекта, достаточно скорректировать значение константы CURRENT_OBJECTS_NUM (она должна быть равна количеству объектов, загружаемых в программе) и написать ряд команд, аналогичных уже имеющимся (которые загружают тестовые объекты). В функции InitObject:

if typ='Название пользовательского объекта' then

objects[index].LoadMeshFromFile('путь к Х-файлу пользовательского объекта', 'путь к текстуре пользовательского объекта');

Далее, в разделе «Var» функции InitGraphObject необходимо добавить еще одну переменную типа D3DMATRIX. Эта переменная устанавливает положение объекта. Далее необходимо задать ее значения и вызвать метод SetWorldPos, в параметре которого указать заполненную матрицу. Объект установлен.

Если пользователю нужен новый источник освещения, то в разделе «Var» функции InitGraphObject нужно указать еще одну переменную типа TD3DXVECTOR3 и заполнить ее значением направления нового источника света. Чтобы установить новую лампочку нужно вызвать метод «движка» объекта SetLightPos. Пример:

lp:=D3DXVECTOR3(20, 20, 0);

objects[n].SetLightPos(lp, l);

Где lp - вектор направления новой лампочки, n - номер объекта, а l - индекс лампочки.

Устройства ввода

Чтобы назначить действие какой-либо клавиши клавиатуры или мыши, нужно добавить одну строчку (в функцию «движка» игры KeyControl), в которой сначала идет проверка на нажатие нужной клавиши, а далее, если клавиша нажата, действие. Пример:

if bKeyBuffer[DIK_Клавиша] = $080 then Действие;

Константы, которые определяют клавиши можно найти в заголовочном файле DirectInput. Содержимое этого файла можно посмотреть на прилагаемом диске в папке DirectX\Borland_D6-7. Для добавления действий по нажатию клавиш мыши не нужно писать проверку, она уже есть в функции, осталось только добавить код действия в блок begin end; Пример:

if ms.rgbButtons[0] = $080 then begin действие end;

Эта программная строчка определяет действие при нажатии 0-й клавиши мыши.

 

3.4.2 Как использовать «движок» в собственном проекте

Первое действие, которое необходимо совершить, для использования модуля движка в собственной программе - это подключение его к программе, в файле проекта. Как это сделать, можно разобраться по примеру файла проекта данной программы (Game.dpr).

1.  В разделе uses нужно подключить модули DirectX и «движка»;

2.      В разделе «var» главной программы объявить переменную, для хранения класса движка (в данной программе это переменная «ge»);

.        В программе вызвать конструктор движка, на этапе инициализации;

.        Вызвать функцию формирования сцены (ge.RenderScene).

Рекомендации по возможностям расширения движка

Для реализации алгоритма проверки столкновений в игре удобнее всего модернизировать функцию MoveCamera, так как именно в этой функции происходит перемещение камеры по виртуальному миру. Для проверки столкновений нужно сначала получить точки, по которым будет определяться, произошло ли столкновение. Для этого лучше всего подойдет функция LoadMeshFromFile, которая находится в модуле DXGObject. В нее можно добавить получение данных из Х-файла и точек окружности или куба, в которые будут вписаны объекты (это зависит от того, какой алгоритм проверки столкновений решит реализовать пользователь).

Так же в функцию модуля «движка» объекта LoadMeshFromFile можно добавить и поддержку анимации, то есть загрузку данных о скелете (если он есть) или загрузку анимации из Х-файла.

Функции действий пользователь легко может добавить в класс «движка» игры или «движка» объекта.

 

3.4.3 Запуск exe-файла

Для успешного запуска откомпилированного файла необходимо, чтобы на компьютере был установлен DirectX 9, все файлы сеток и текстур были на месте и, поскольку в программе используются функции модуля D3DX9, необходимо, чтобы в одной папке с проектом находились две библиотеки dll - D3DX9_27.dll и D3DX9sab.dll. Если планируется дальнейшее использование модуля D3DX9 в своих проектах, пользователю рекомендуется скопировать эти две библиотеки в папку system32, тогда не возникнет необходимости копировать их в каждую папку с проектом.

При запуске проекта сначала будет произведен запрос на отображение в полноэкранном режиме, и если нажата кнопка «Yes», то произойдет запуск программы в полноэкранном режиме, иначе (если «No») соответственно в оконном.


Далее происходит прорисовка окна и формирование сцены.

Если программа не запустилась и выдала ошибку на этапе инициализации, значит аппаратное и/или программное обеспечение компьютера не соответствует требованиям программы. В таком случае можно попробовать установить более свежую версию DirectX 9. Если и это не помогло, то программа не запустится.

После отображения окна, можно начинать работу в приложении. Перемещение по миру происходит с помощью клавиш: ‘w’- вверх, ‘s’- вниз, ‘a’ - влево, ‘d’ - вправо. Поворот камеры происходит при нажатии клавиш «стрелок» на клавиатуре и при движении мышью. По нажатию клавиши ‘Esc’ происходит завершение работы приложения.

 

3.5 Оценка результатов решения задачи


Проект успешно выполняет все действия, которые ставились целью на этапе постановки задачи:

1.  Инициализация графики;

2.      Загрузка объектов;

.        Обработка данных, полученных с устройств ввода.

Поставленная задача выполнена.

ЭКОНОМИЧЕСКАЯ ЧАСТЬ

Расчет предполагаемой прибыли от реализации созданного программного продукта

Проект предназначен для компании «Маклай».

Данный программный продукт может принести предприятию определенные плюсы:

·              Упрощение разработки приложений

·              Уменьшение временных затрат на производство

·              Качественный результат

4. Описание и расчет затрат на выполнение проекта


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

 

5. Определение трудоёмкости


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

Форма расположения работ по этапам:

.Разработка технического задания (ТЗ):

получение ТЗ;

. Подготовительный этап:

сбор информации;

разработка общей методики создания продукта;

. Основной этап:

Разработка основного алгоритма;

Отладка;

. Завершающий этап:

Подготовка технической документации;

сдача продукта.

Трудоемкость выполнения работы оценивается экспертным путем в человеко-днях и носит вероятностный характер. Определим трудоёмкость по видам работ.

Таблица №1: Расчет трудоёмкости по проекту

Виды работ

Трудоёмкость, чел/час

получение ТЗ

2

сбор информации и ознакомление с предметной областью

50

выбор объектного построения программы

10

разработка общей методики создания продукта

12

разработка основного алгоритма

100

Отладка

20

подготовка технической документации

10

сдача продукта

1

Итого

205


Суммарная трудоемкость составила 205 чел.\час.

Расчет расходов на заработную плату.

На основе данных о трудоемкости и средней заработной плате по отрасли рассчитываем основную заработную плату. Предположим, что заработная плата программиста без опыта работы составляет 25000 тыс. руб. в месяц. (21 рабочий день, 8 часовой рабочий день) или 148,8 руб./час.

Таким образом, расходы на заработную плату по нашему проекту составляют:

ЗП = 148,8 *125 = 18600 руб.

Отчисления на ЗП (Пенсионный Фонд, Фонд соц. страхования, Фонд обязательного мед. страхования, территории. фонды мед. страхования) составляют 26% (20,0+2,9+1,1+2,0 соответственно). В денежном выражении составляют: = 18600 *0,26= 4836 руб.

Рассчитаем затраты на материалы:

Материалы, затраченные на создание проекта, приведены в таблице 2.

Таблица №2: Расчет затрат на материалы

Расходные материалы:

Количество:

Цена за единицу, руб:

Сумма, руб:

бумага а4

1 упаковка

150

картридж для принтера

2 штуки

500

1000

диск

1 штука

20

15

папка

1 штука

100

100

файлы

100 штук

1

100

Итого



1365


Рассчитаем амортизацию.

Стоимость компьютера 24 тыс. руб. Используем его в течении 5 лет (60 мес.). Годовая амортизация составит в месяц 400 руб. В час: 2,38 руб. Умножив на трудоёмкость, определим:

АО = 2,38*125= 297,5 руб.

Рассчитаем расходы на электроэнергию.

ПК в среднем потребляет 0,6 Квт/час. 0,6*135=81КВт. Примерная стоимость 1 Квт/ч около 1,97 руб.

Итого: 81*1,97=159,57руб.

Все результаты расчета затрат приведены в таблице 3.

Таблица №3: Смета всех затрат

Наименование статей затрат

Сумма, руб.

Расходные материалы

1365

Основная заработная плата

18600

Расходы на электроэнергию

159,57

Амортизационные отчисления

297,5

Отчисления на ЗП

4836

Итого

25258,07


Таким образом, суммарная стоимость затрат на создание данного программного продукта составляет 25258,07 рублей, следовательно эту программу ниже этой цены продать нельзя, так как в этом случае программа не будет являться экономически эффективной.

Рыночная стоимость аналогичного программного продукта составляет 35000 рублей.

Таким образом, реализовав наш программный продукт, мы можем получить прибыль в размере 9742 рубля.

 

6. Заключение


Существует множество графических «движков» для 3D игр, написанных на различных языках, использующих различные технологии. Среди них не так много написаны на языке программирования Delphi под DirectX. На пути к осуществлению конечной цели, которой являлась разработка графического движка, возникло множество проблем, связанных с нехваткой учебной информации и практических примеров. Было переведено множество кодов с языка С++, разобраны различные алгоритмы организации программы. Теперь эта работа поможет молодым программистам, желающим работать в этом направлении. С ее помощью они смогут освоить программирование 3D игр под DirectX на Delphi и использовать эти знания и опыт в своих проектах.

Написание «движка» игры еще не завершено. В этой работе еще нет просчета столкновений и искусственного интеллекта, который просчитывал бы действия противника. Эти два пункта обязательно должны присутствовать в игровой программе, но их написание слишком сложный процесс, требующий больших затрат времени (обычно этим занимается не один программист).

Можно отметить, что процесс написания любой программы никогда нельзя назвать завершенным. Всегда существуют множество направлений для расширения функций программы. Графический движок - это только начальная стадия написания полноценной 3D игры. Этот движок обладает массой возможностей для расширения и модернизации. И даже написанная с его помощью, полноценная трехмерная игра всегда будет иметь возможности для расширения и оптимизации.

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

Приложение №1


Результат выполнения программы

Запрос на полноэкранный режим

Окно программы

Приложение №2


Исходный код файла проекта Game.dpr

program Game;,,

Direct3D9, //модуль основных функций Direct3D, //модуль инициализации Direct3D и загрузки Х-файлов; //модуль "движка" игры

{$R *.RES}: TWndClassEx; //класс окна: HWND; //окно: TMsg; //сообщения Windows

//Переменные для работы с Direct3D

pD3D: IDirect3D9=nil;DDevice: IDirect3DDevice9=nil;

//Переменная движка игры:CGraphEngine;

Procedure GraphEngine(); //процедура реализации движка(SUCCEEDED(pD3DDevice.BeginScene())) then //Если процедура

//BeginScene была выполнена, то вызываем движок

begin

//очистка экранаDDevice.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER , D3DCOLOR_XRGB(255,255,255), 1.0, 0);

ge.RenderScene(); //формирование сцены

pD3DDevice.EndScene(); //конец отображения сцены;DDevice.Present(nil, nil, 0, nil); //Функция для отображения сцены, без

//нее сцена не сформируется;Init; //Функция инициализации графикиb:boolean; //вспомогательная переменная

begin:=false;

//инициализация графики(DX3DInit(pD3D, pD3DDevice, pWnd, 800, 600, b)<>true) then

begin //если процедура инициализации вернула false, значит произашла

//ошибка

MessageBox(pWnd, 'Ошибка инициализации DirectX?', 'Error', MB_OK); //выводим сообщение об ошибке

exit; //выходим из программы;

ge:=CGraphEngine.CGraphEngine(pD3DDevice, pWnd); //вызываем

//конструктор движка;WindowProc(wnd: HWND; Msg: Integer; wParam: wParam; lParam: lParam): Lresult; stdcall;:=0;msg of_CREATE:

begin;_DESTROY: //Событие завершения програмы. Освобождаем

//захваченые ресурсыDDevice := nil;

pD3D := nil;(0);();;;:=DefWindowProc(wnd,msg,wparam,lparam);

end;;

//заполняем структуру TWndClassEx для определения параметров окна.cbSize := sizeof(wc); //размер структуры

wc.style:=cs_classdc;.lpfnWndProc := @WindowProc;.cbClsExtra := 0;.cbWndExtra := 0;.hInstance := HInstance;.hCursor := LoadCursor(0, IDC_ARROW);.hbrBackground:= COLOR_BTNFACE+1;.lpszMenuName := nil;.lpszClassName:= 'Room Example';(wc);

//создание окна:= CreateWindowEx(WS_EX_APPWINDOW, 'Room Example', 'Delphi DirectX Game',_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX, 0, 0, 250, 130, 0, 0,, nil);; //инициализация графики(pWnd, SW_normal); //Показать окно

UpdateWindow(pWnd); //Обновить окно

//Цикл обработки сообщений (бесконечен)

while (true) do

begin

if (PeekMessage(pmsg, 0, 0, 0, PM_REMOVE)) then //если есть

//сообщение, то обрабатываем его

begin

TranslateMessage(pmsg);

DispatchMessage(pmsg);

if (pmsg.message = WM_QUIT) then exit; //если нажата

//клавиша выхода, то завершаем работу программы

end

GraphEngine(); //вызываем функцию движка

end;.

Приложение №3


Исходный код модуля «движка» игры GraphEngine.pas

unit GraphEngine;,D9, //модуль основных функций Direct3DDX9, //модуль разширенных функций Direct3D, //модуль для работы с устройствами ввода

Classes,

DXTypes, //Модуль, содержащий некоторые дополнительные типы

DXGObject, //Модуль "движка" объекта

dxfunc; //Модуль инициализации графики

const NUM_OBJECTS = 20; //максимальное колличество объектов

CURRENT_OBJECTS_NUM = 2; //реальное колличество объектов

type

CGraphEngine = class //создаем класс "движка" игры

private

// Input variable:IDirect3DDevice9; //устройтво Direct3D:array [0..NUM_OBJECTS] of CDXGObject; //массив игровых

//объектовInitObject(typ:string;index:integer):boolean; //функция

//инициализации объектов

procedure InitGraphObjects(); //функция установки параметров вывода

//объектов

function KeyControl():boolean; //Функция для обработки данных,

//полученных с устройств ввода

procedure MoveCamera(fOffset:real; vecModifier:TD3DXVECTOR3); //Функция перемещения камеры

procedure RotateCamera(fOffset:real; Axis:integer); //Функция поворота

//камерыCGraphEngine(pD3DDevice:IDirect3DDevice9; hWnd: HWND); overload;CGraphEngine(); overload;RenderScene(); //Функция отображения сцены;: IDirectInput8 = nil; //главное устройтво DirectInput: IDirectInputDevice8 = nil; //клавиатура: IDirectInputDevice8 = nil; //мышь, xrMat, yrMat, mvMat:D3DMATRIX; //матрици для преобразования, vecLR, vecFB, up: TD3DXVECTOR3; //вектора направления осмотра

procedure InitDirectInput( hWnd: HWND ); //функция инициализации

//устройств вводаReleaseDirectInput(); //Функция освобождения захваченых DirectInput ресурсовCGraphEngine.CGraphEngine(pD3DDevice:IDirect3DDevice9; hWnd: HWND); w:D3DMATRIX;

begin

//назначаем переменной pDevice устройство, созданное в главной программе

pDevice:=pD3DDevice;

// инициализация векторов и матриц

pos := D3DXVECTOR3(0.0, 0.0, 0.0);:= D3DXVECTOR3(10.0, 0.0, 0.0);:= D3DXVECTOR3(0.0, 0.0, -10.0);:= D3DXVECTOR3(0.0, 10.0, 0.0);DXMatrixIdentity(xrMat);DXMatrixIdentity(yrMat);DXMatrixIdentity(mvMat);

// Init view matrix._41:=20;._42:=-10;

w._43:=90;

worldMat:=w;

//Инициализируем объекты

InitGraphObjects();

//Инициализируем устройства ввода

InitDirectInput(hWnd);

end;CGraphEngine.CGraphEngine();i:integer;i:=0 to CURRENT_OBJECTS_NUM do

objects[i]:=nil; //удаляем все игровые объекты

end;CGraphEngine.InitGraphObjects();,pos1,rot:D3DMATRIX;,lp1,lp2:TD3DXVECTOR3;

begin

// инициализация объектов

InitObject('SO_DEFAULT_ROOM', 0);('SO_DX_MAN', 1);

// позиция комнаты

Pos._11:=0.1; Pos._12:=0; Pos._13:=0; Pos._14:=0;

Pos._21:=0; Pos._22:=0.1; Pos._23:=0; Pos._24:=0;._31:=0; Pos._32:=0; Pos._33:=0.1; Pos._34:=0;._41:=0; Pos._42:=0; Pos._43:=0; Pos._44:=10;

//устанавливаем объект (комнату) в позицию

objects[0].SetWorldPos(Pos);

//позиция человека._11:=0.1; Pos1._12:=0; Pos1._13:=0; Pos1._14:=0;._21:=0; Pos1._22:=0.1; Pos1._23:=0; Pos1._24:=0;._31:=0; Pos1._32:=0; Pos1._33:=0.1; Pos1._34:=0;1._41:=0; Pos1._42:=0; Pos1._43:=0; Pos1._44:=1;

//поворачиваем объект (человека)

D3DXMatrixRotationX(Rot, -1.5);DXMatrixMultiply(Pos1, Pos1, Rot);

//устанавливаем объект (человека) в позицию

objects[1].SetWorldPos(Pos1);

//Назначаем векторы направления освещения

lp:=D3DXVECTOR3(20, 20, 0);

lp1:=D3DXVECTOR3(8, 30, 15);

lp2:=D3DXVECTOR3(-8, -30, -15);

//Устанавливаем источники освещения

objects[0].SetLightPos(lp, 0);[1].SetLightPos(lp1, 1);

//objects[0].SetLightPos(lp2, 2);;CGraphEngine.InitObject(typ:string;index:integer):boolean;[index]:=CDXGObject.CDXGObject(pDevice);typ='SO_DEFAULT_ROOM' then[index].LoadMeshFromFile('media\room.x', 'media\doski.bmp');typ='SO_DX_MAN'then[index].LoadMeshFromFile('media\tiny.x', '');:=true;;CGraphEngine.RenderScene(); i:integer;

begin

//если функция KeyControl вернула false, значит дальнейшее выполнение программы

//не имеет смысла (программа не реагирует на устройства ввода)

if KeyControl=false then exit; //выходим из программы если функция KeyControl вернула false

//Отображаем сцену.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

//Очищаем переменную устройства Direct3D.Clear(0, nil, D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 1.0, 0);

//Отображаем объекты на экранеi:=0 to CURRENT_OBJECTS_NUM-1 do

 objects[i].Render(0);;

//////////////////////////

//directInput////////

//////////////////////////InitDirectInput( hWnd: HWND );: TDIPROPDWORD; // Структура для задания характеристик мыши

// Создаём главный объект DirectInputFAILED(DirectInput8Create(GetModuleHandle(nil), DIRECTINPUT_VERSION, IID_IDirectInput8, lpDI8, nil ) ) then;

lpDI8._AddRef();

// Создаём объект для работы с клавиатурой

if FAILED( lpDI8.CreateDevice( GUID_SysKeyboard, lpDIKeyboard, nil ) ) then;

lpDIKeyboard._AddRef();

// Устанавливаем предопределённый формат для "простогй клавиатуры".

// В большинстве случаев можно удовлетвориться и установками,

//заданными в структуре

// c_dfDIKeyboard по умолчанию, но в особых случаях нужно заполнить её

//самомуFAILED( lpDIKeyboard.SetDataFormat( c_dfDIKeyboard ) ) then;

// Устанавливаем уровень кооперации. Подробности о флагах смотри в

//DirectX SDKFAILED(lpDIKeyboard.SetCooperativeLevel(hWnd, DISCL_BACKGROUND or DISCL_NONEXCLUSIVE ) ) then;

// Захвытываем клавиатуру.Acquire();

// Создаём объект для работы с мышью

if FAILED( lpDI8.CreateDevice(GUID_SysMouse, lpDIMouse, nil)) then;

lpDIMouse._AddRef();

// Устанавлаваем предопределённый формат данных

if FAILED( lpDIMouse.SetDataFormat(c_dfDIMouse)) then;

// Устанавливаем уровень кооперации для мыши

if FAILED(lpDIMouse.SetCooperativeLevel(hWnd, DISCL_FOREGROUND or_EXCLUSIVE)) then;

// Захвытываем мышь.Acquire();;ReleaseDirectInput();

// Удаляем объект для работы с клавиатурой

if lpDIKeyboard <> nil then.Unacquire(); // Освобождаем устройство._Release();:= nil;;

// Удаляем объект для работы с мышью

if lpDIMouse <> nil then.Unacquire();._Release();:= nil;;

// Последним удаляем главный объект DirectInput

if lpDI8 <> nil then

begin._Release();:= nil;;;CGraphEngine.KeyControl():boolean; View:D3DMATRIX; //матрица положения камеры

bKeyBuffer: array [0..255] of Byte; //буфер-массив клавиш клавиатуры

ms: TDIMOUSESTATE; //переменная для данных, полученных от мыши

begin

//присваеваем результату работы функции значение false

Result := FALSE;

// Производим опрос состояния клавиш, данные записываются в буфер

//массив.GetDeviceState( SizeOf( bKeyBuffer ), @bKeyBuffer );

//Если нажата клавиша Esc выход из программы

if bKeyBuffer[DIK_ESCAPE] = $080 then PostQuitMessage(0);

// Изменяем положение камеры в зависимости от нажатой клавиши

if bKeyBuffer[DIK_W] = $080 then MoveCamera(0.05, vecFB);bKeyBuffer[DIK_S] = $080 then MoveCamera(-0.05, vecFB);bKeyBuffer[DIK_A] = $080 then MoveCamera(0.05, vecLR);bKeyBuffer[DIK_D] = $080 then MoveCamera(-0.05, vecLR);bKeyBuffer[DIK_left] = $080 then RotateCamera(-0.05, 0);bKeyBuffer[DIK_RIGHT] = $080 then RotateCamera(0.05, 0);bKeyBuffer[DIK_UP] = $080 then RotateCamera(0.05, 1);bKeyBuffer[DIK_DOWN] = $080 then RotateCamera(-0.05, 1);

//производим опрос состояния мыши.GetDeviceState(SizeOf(TDIMOUSESTATE), @ms);

//поворачиваем камеру в зависимости от движений мыши

if ms.lX<0 then RotateCamera(-0.05, 0) elsems.lX>0 then RotateCamera(0.05, 0);ms.lY<0 then RotateCamera(0.05, 1) elsems.lY>0 then RotateCamera(-0.05, 1);ms.rgbButtons[0] = $080 then begin end;ms.rgbButtons[1] = $080 then begin end;ms.rgbButtons[2] = $080 then begin end;ms.rgbButtons[3] = $080 then begin end;

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

pos := D3DXVECTOR3(0.0, 0.0, 0.0);

vecLR := D3DXVECTOR3(10.0, 0.0, 0.0);:= D3DXVECTOR3(0.0, 0.0, -10.0);:= D3DXVECTOR3(0.0, 10.0, 0.0);

//перемножаем матрици:=worldMat;DXMatrixMultiply(View, xrMat, yrMat);DXMatrixMultiply(View, View, mvMat);

//Трансформируем координатыDXVec3TransformCoord(pos, pos, View);DXVec3TransformCoord(vecFB, vecFB, View);DXVec3TransformCoord(vecLR, vecLR, View);DXVec3TransformNormal(up, up, View);

//Устанавливаем положение камерыDXMatrixLookAtLH(View, pos, vecFB, up);.SetTransform(D3DTS_VIEW, View);

//Включаем освещение.SetRenderState(D3DRS_LIGHTING, DWord(true));

//функция выполнира работу успешно и присваеваем результату значение true

result:=true;;CGraphEngine.MoveCamera(fOffset:real;:TD3DXVECTOR3);tView:TD3DXMATRIX; //матрица преобразования

vect:TD3DXVECTOR3; //вектор положения камеры

begin

D3DXVec3Subtract(vect, vecModifier, pos);

//корректируем значение вектора положения камеры

vect.x:=vect.x*fOffset; vect.y:=0; vect.z:=vect.z*fOffset;

D3DXMatrixTranslation(tView, vect.x, vect.y, vect.z); //устанавливаем положение камеры с учетом новой позиции

D3DXMatrixMultiply(mvMat, mvMat, tView); //устанавливаем камеру в новую позицию

end;CGraphEngine.RotateCamera(fOffset:real; Axis:integer);tView:TD3DXMATRIX;(Axis) of

:beginDXMatrixRotationY(tView, fOffset); //поворачиваем камеру на

//коэффициент fOffsetDXMatrixMultiply(yrMat, yrMat, tView); //устанавливаем камеру в

//новую позицию по оси у

exit;

end;

:beginDXMatrixRotationX(tView, fOffset);DXMatrixMultiply(xrMat, xrMat, tView); //устанавливаем камеру в

//новую позицию по оси х

exit;

end;;;.

 

Приложение №4


Исходный код модуля «движка» Объекта DXGObject.pas

unit DXGObject;,,D9, //модуль основных функций Direct3DDX9, //модуль разширенных функций Direct3D,

DXTypes, //Модуль, содержащий некоторые дополнительные типы; //Модуль инициализации графикиMAX_OBJECT_EFFECTS = 10; //максимальное колличество эффектов

//(используется при установки освещения)

type

asingle= array [0..2] of single;= class //создаем класс движка объекта

// 3D Устройство:IDirect3DDevice9;

// Сетка Mesh:DWord; //номер материала:ID3DXMesh; //сетка

pMeshTextures:PAIDirect3DTexture9; //Текстура:PAD3DMATERIAL9; //материал:array [0..MAX_OBJECT_EFFECTS] of IDirect3DTexture9; //массив

//текстур:array [0..MAX_OBJECT_EFFECTS] of D3DLIGHT9; //массив

//источников света

// Мировая матрица

matWorld:D3DMATRIX;

constructor CDXGObject(pD3DDevice:IDirect3DDevice9);LoadMeshFromFile(filename:string; texturename:string); //Процедура Загрузки Х-файлаLoadTexture(filename:PAnsiChar; index:integer); //Процедура

//Загрузки текстуры

function GetWorldPos():D3DMATRIX ; //Функция получения позиции

//мира (мировой матрици)SetWorldPos(matrix:D3DMATRIX); //Процедура определения

//положения объектаSetLightPos(Pos:TD3DXVECTOR3; index:integer); //Процедура установки источника освещенияPositioning(); //Процедура установки позиции объекта

function Render(ef_index:DWORD=1):boolean; //Функция отображения

// объекта;CDXGObject.CDXGObject(pD3DDevice:IDirect3DDevice9);:=pD3DDevice; //получаем образец устройства Direct3DDXMatrixIdentity(matWorld); //инициализируем мировую матрицу;CDXGObject.LoadTexture(filename:PAnsiChar; index:integer);

//Загрузка текстуры из файлаDXCreateTextureFromFileEx(pDevice, filename, D3DX_DEFAULT,DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,DX_FILTER_TRIANGLE or D3DX_FILTER_MIRROR,DX_FILTER_TRIANGLE or D3DX_FILTER_MIRROR,

, nil, nil, pTexture[index]);;CDXGObject.SetLightPos(Pos:TD3DXVECTOR3; index:integer);

begin

//выделяем память под сткуктуру источника света

ZeroMemory(@light[index], sizeof(D3DLIGHT9));

//Заполняем структуру источника света (задаем параметры параметры)

light[index]._Type := D3DLIGHT_DIRECTIONAL; //тип освещения (рассеяный свет)[index].Direction := pos; //Направление[index].Diffuse.r :=1; //Цвет (белый)[index].Diffuse.g :=1;[index].Diffuse.b :=1;

light[index].Diffuse.a :=1;

//Устанавливаем источник освещения

pDevice.SetLight(index, light[index]);.LightEnable(index, true);;CDXGObject.GetWorldPos():D3DMATRIX;:=matWorld; //получаем мировую матрицу;CDXGObject.SetWorldPos(matrix:D3DMATRIX);

begin:=matrix; //определяем мировую матрицу

end;CDXGObject.Positioning();.SetTransform(D3DTS_WORLD, GetWorldPos()); //Устанавливаем

//матрицу позиции объекта;CDXGObject.Render(ef_index:DWORD):boolean;

var i: integer;

//присваеваем результату работы функции значение false

Result:= false;();

// отображение объектаi:=0 to dwNumMaterials-1 do

pDevice.SetMaterial(pMeshMaterials[i]); //устанавливаем материал

if pMeshTextures[i]<>nil then //устанавливаем текстуру, если она есть

pDevice.SetTexture(0, pMeshTextures[i]);.DrawSubset(i); //Рисуем сетку

end;

//функция выполнира работу успешно и присваеваем результату значение true

result:=true;;CDXGObject.LoadMeshFromFile(filename:string; texturename:string);:= LoadMesh(filename, pDevice, pMesh, pMeshTextures,

texturename, pMeshMaterials); //загрузка Х-файла;

end.

Приложение №5


Исходный код модуля инициализации графики и загрузки Х-файлов dxfunc.pas.

unit dxfunc;Direct3D9, D3DX9, windows, SySUtils;

type

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

PAIDirect3DTexture9=^AIDirect3DTexture9;DTexture9=array[0..0] of IDirect3DTexture9;

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

AD3DMATERIAL9 = array [0..0] of TD3DMATERIAL9;DMATERIAL9 = ^AD3DMATERIAL9;

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

AD3DXMATERIAL = array [0..0] of TD3DXMATERIAL;DXMATERIAL = ^AD3DXMATERIAL;

//функция инициализации Direct3DDX3DInit(var ppiD3D9: IDirect3D9; //переменные для работы с DirectXppiD3DDevice9: IDirect3DDevice9;: THandle; //Переменная, для хранения параметров окна

iWidth, iHeight: Integer; //Разрешение (размер создаваемого окна)bFullScreen: Boolean):Boolean; //параметр, указывающий на то, нужно ли выводить программу в полноэкранном режиме

//Функция загрузки Х-файлаLoadMesh(filename:String; //Имя Х-файлаDDevice9:IDirect3DDevice9; //Переменная для работы с DirectXppMesh:ID3DXMesh; //СеткаpMeshTextures: PAIDirect3DTexture9; //Текстура

texturefilename: String; //Имя файла bmp текстурыpMeshMaterials: PAD3DMATERIAL9):DWORD; //Материал

//Функция загрузки текстурыLoadTexture(_buf:PByteArray;, iHeight:Integer;:String):Boolean;DX3DInit(var ppiD3D9: IDirect3D9;ppiD3DDevice9: IDirect3DDevice9;: THandle;, iHeight: Integer;bFullScreen: Boolean):Boolean;

vardpp:TD3DPresentParameters;: Integer; //Используется для хранения ответа пользователя на

//запрос отображения, clientRect: TRect;ddm : TD3DDISPLAYMODE;: DWORD; //дополнительный флаг: HRESULT;: real; //Номер адаптера : TD3DMATRIX; //Матрица проекции

//присваеваем результату работы функции значение false

Result:= false;D9:=Direct3DCreate9(D3D_SDK_VERSION); //создаем устройство Direct3D

if ppiD3D9=nil then //есди устройство не созданно, то завершаем работу

//функции;

// Заполняем основные параметры представления(@d3dpp, sizeof(d3dpp));dpp.BackBufferWidth := iWidth; //ширина заднего буфераdpp.BackBufferHeight := iHeight; //Высота заднего буфера

d3dpp.AutoDepthStencilFormat := D3DFMT_D16;dpp.EnableAutoDepthStencil := TRUE;

// Запрос на отображение в полноэкранном режиме

if (bFullScreen=false) then:=MessageBox(hWnd, 'Use fullscreen mode?', 'Screen', MB_YESNO or MB_ICONQUESTION):= IDYES;(iRes = IDYES) then

begin

//////////////////////////////////////////////////////////

// Полноэкранный режим

//////////////////////////////////////////////////////////

// Установка параметров полноэкранного режима

d3dpp.BackBufferFormat := D3DFMT_R5G6B5;

d3dpp.SwapEffect := D3DSWAPEFFECT_FLIP;

d3dpp.Windowed := FALSE;dpp.FullScreen_RefreshRateInHz := D3DPRESENT_RATE_DEFAULT;dpp.PresentationInterval := D3DPRESENT_INTERVAL_DEFAULT;

//////////////////////////////////////////////////////////

// Оконный режим

//////////////////////////////////////////////////////////(hWnd, wndRect);(hWnd, clientRect);

//Задаем разрешение (размеры) окна

iWidth := iWidth + (wndRect.right-wndRect.left) - (clientRect.right-clientRect.left);

iHeight := iHeight + (wndRect.bottom-wndRect.top) - (clientRect.bottom-clientRect.top);

//создать окно

MoveWindow(hWnd, wndRect.left, wndRect.top, iWidth, iHeight, TRUE);

// Получить формат пикселя

ppiD3D9.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, d3ddm);

// Установка параметров

d3dpp.BackBufferFormat := d3ddm.Format;

d3dpp.SwapEffect := D3DSWAPEFFECT_DISCARD;

d3dpp.Windowed := TRUE;;:= D3DCREATE_MIXED_VERTEXPROCESSING;

// Создать 3D устройство:= ppiD3D9.CreateDevice(

 D3DADAPTER_DEFAULT,

D3DDEVTYPE_HAL, hWnd, Flags,

@d3dpp, ppiD3DDevice9);(FAILED(hRes)) then

exit;

// Установить перспективу:= d3dpp.BackBufferWidth / d3dpp.BackBufferHeight;DXMatrixPerspectiveFovLH(matProjection, D3DX_PI/4.0, Aspect, 0.1, 2000.0);DDevice9.SetTransform(D3DTS_PROJECTION, matProjection);

//Отключить освещениеDDevice9.SetRenderState(D3DRS_LIGHTING, DWORD(FALSE));

//функция выполнира работу успешно и присваеваем результату значение true

Result:=true;;LoadMesh(filename:String;DDevice9:IDirect3DDevice9;ppMesh:ID3DXMesh;pMeshTextures: PAIDirect3DTexture9;: String;pMeshMaterials: PAD3DMATERIAL9):DWORD;DXMtrlBuffer: ID3DXBUFFER;: DWORD;dxMaterials: PAD3DXMATERIAL;:Integer;

begin

//Чтение данных из Х-файла

D3DXLoadMeshFromX(PAnsiChar(filename), D3DXMESH_SYSTEMMEM, ppiD3DDevice9,, @pD3DXMtrlBuffer, nil, @dwNumMaterials, ppMesh);dxMaterials := pD3DXMtrlBuffer.GetBufferPointer();

// Инициализируем материалы и текстуры(pMeshMaterials, SizeOf(TD3DMATERIAL9)*dwNumMaterials);(pMeshTextures, SizeOf(IDIRECT3DTEXTURE9)*dwNumMaterials);i:=0 to dwNumMaterials-1 do

// Копируем материал[i] := d3dxMaterials[i].MatD3D;

// Создаем текстуру, если она есть(FAILED(D3DXCreateTextureFromFile(ppiD3DDevice9,dxMaterials[i].pTextureFilename, pMeshTextures[i]))=true) then(FAILED(D3DXCreateTextureFromFile(ppiD3DDevice9,(texturefilename), pMeshTextures[i]))) then[i] := nil;;:=dwNumMaterials;;LoadTexture(text_buf:PByteArray;iWidth, iHeight:Integer; filename:String):Boolean;:THandle;:BITMAPFILEHEADER;:DWORD;:String;:BITMAPINFOHEADER;, bytesgiven, i, p:Integer;:array of byte;:PByte;:PByteArray;: PWord;:PRGBTRIPLE;:=false;:= CreateFile(PAnsiChar(filename), GENERIC_READ, 0, nil,_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);(hFile = INVALID_HANDLE_VALUE) then;(ReadFile(hFile, bmpfilehdr, sizeof(bmpfilehdr), dwRead, nil)=false) then;:=Chr(bmpfilehdr.bfType);(ptr[1]<>'B') then;

// Проверяем заголовок изображения(ReadFile(hFile, bmpinfohdr, sizeof(bmpinfohdr), dwRead, nil)=false) then;(bmpinfohdr.biCompression<>BI_RGB) then;:=bmpinfohdr.biSizeImage;(iImageSize=0) then:=iWidth*bmpinfohdr.biBitCount*iHeight;

// Читаем данные изображения в буфер(buf,iImageSize);

if (ReadFile(hFile, buf[0], iImageSize, dwRead, nil)=false) then;(bmpinfohdr.biBitCount<>24)then;:=(iWidth*3+3) and (not 3);:= PByte(text_buf);:= @buf[(iHeight-1)*bytesgiven];i:=0 to iHeight-1 do:=PWord(btSurf);:=PRGBTRIPLE(imagebits);p:=0 to iWidth-1 do(wdSurf)^ := (tlImage.rgbtBlue) +

(tlImage.rgbtGreen SHL 8) +

(tlImage.rgbtRed SHL 16)+

$FF000000;:=Pointer(Integer(tlImage)+3);:=Pointer(Integer(wdSurf)+4);;:= Pointer(Integer(btSurf)+iWidth*4);:= Pointer(Integer(imagebits)-bytesgiven);;:=true;;.

Похожие работы на - Программа – трехмерный игровой движок на базе библиотек DirectX и PhysX

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!