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

бесплатно 0
4.5 121
Разработка игрового "движка" с использованием языка C для написания кода, графического пакета DirectX9 для вывода графики. Использование физического "движка" PhysX для взаимодействия объектов. Технико-математическое описание задачи, листинг программы.

Скачать работу Скачать уникальную работу

Чтобы скачать работу, Вы должны пройти проверку:


Аннотация к работе
Так же Visual Studio предлагает удобные средства отладки программы, средства предварительной компиляции шейдеров на языке HLSL и удобны менеджер объектов, функций и классов - INTELLISENSE. Ниже представлены все ключевые слова в языке С . 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 Программа «движка» должна инициализировать основные устройства ввода (клавиатура, мышь), считывать из них информацию, и, в соответствии с введенными пользователем данными, производить определенные действия (например, при нажатии клавиши «left» сместить камеру влево). Эта программа будет использоваться другими программистами в их программах, и должна подходить как можно большему числу проектов. //не имеет смысла (программа не реагирует на устройства ввода) if KEYCONTROL=false then exit; //выходим из программы если функция KEYCONTROL вернула falseProcedure GRAPHENGINE(); //процедура реализации движка begin if (SUCCEEDED(PD3DDEVICE.

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

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

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

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

Данная работа посвящена разработке игрового движка. Для написания кода используется язык 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.2.5 Дополнительные средства языка

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

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

DIRECTX

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

Direct3D

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

DIRECTDRAW

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

DIRECTSOUND

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

DIRECTINPUT

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

DIRECTPLAY

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

DIRECTSETUP

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

DIRECTSHOW

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

Архитектура доступа базируется на технологии 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 if (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. if (SUCCEEDED(PD3DDEVICE.BEGINSCENE())) then //Если процедура BEGINSCENE была выполнена, то вызываем движок begin

//очистка экрана

PD3DDEVICE.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0, 0);

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

PD3DDEVICE.ENDSCENE(); //конец отображения сцены end;

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

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

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

var i:integer;

begin

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

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

//Отображаем сцену

PDEVICE.SETRENDERSTATE(D3DRS_FILLMODE, D3DFILL_SOLID);

PDEVICE.SETSAMPLERSTATE(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

PDEVICE.SETSAMPLERSTATE(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

//Очищаем переменную устройства Direct3D

PDEVICE.Clear(0, nil, D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 1.0, 0);

//Отображаем объекты на экране for 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;

var i: integer;

begin

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

Result:= false;

Positioning();

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

PDEVICE.SETMATERIAL(PMESHMATERIALS[i]); //устанавливаем материал if PMESHTEXTURES[i]nil then //устанавливаем текстуру, если она есть PDEVICE.SETTEXTURE(0, PMESHTEXTURES[i]);

PMESH.DRAWSUBSET(i); //Рисуем сетку end;

//функция выполнира работу успешно и присваеваем результату значение 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; Клавиатура.

LPDIMOUSE: IDIRECTINPUTDEVICE8 = nil; Мышь.

WORLDMAT, 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 о которых позже. Проверка нажатия клавиши производится с

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

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

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

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

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

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

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

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

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

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

uses windows, messages, Direct3D9, //модуль основных функций Direct3D dxfunc, //модуль инициализации Direct3D и загрузки Х-файлов

GRAPHENGINE; //модуль "движка" игры

{$R *.RES} var wc : TWNDCLASSEX; //класс окна

PWND : HWND; //окно

PMSG : TMSG; //сообщения Windows

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

PD3D: IDIRECT3D9=nil;

PD3DDEVICE: IDIRECT3DDEVICE9=nil;

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

Procedure GRAPHENGINE(); //процедура реализации движка begin if (SUCCEEDED(PD3DDEVICE.BEGINSCENE())) then //Если процедура

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

//очистка экрана

PD3DDEVICE.Clear(0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER , D3DCOLOR_XRGB(255,255,255), 1.0, 0);

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

PD3DDEVICE.ENDSCENE(); //конец отображения сцены end;

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

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

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

//инициализация графики if (DX3DINIT(PD3D, PD3DDEVICE, PWND, 800, 600, b)true) then begin //если процедура инициализации вернула false, значит произашла

//ошибка

MESSAGEBOX(PWND, "Ошибка инициализации DIRECTX?", "Error", MB_OK); //выводим сообщение об ошибке exit; //выходим из программы end;

ge:=CGRAPHENGINE.CGRAPHENGINE(PD3DDEVICE, PWND); //вызываем

//конструктор движка end;

function WINDOWPROC(wnd: HWND; Msg: Integer; WPARAM: WPARAM; LPARAM: LPARAM): Lresult; stdcall;

begin

Result:=0;

case msg of

WM_CREATE: begin end;

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

//захваченые ресурсы begin

PD3DDEVICE := nil;

PD3D := nil;

POSTQUITMESSAGE(0);

RELEASEDIRECTINPUT();

exit;

end;

else

Result:=DEFWINDOWPROC(wnd,msg,wparam,lparam);

end;

end;

begin

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

wc.LPFNWNDPROC := @WINDOWPROC;

wc.CBCLSEXTRA := 0;

wc.CBWNDEXTRA := 0;

wc.HINSTANCE := HINSTANCE;

wc.HCURSOR := LOADCURSOR(0, IDC_ARROW);

wc.HBRBACKGROUND:= COLOR_BTNFACE 1;

wc.LPSZMENUNAME := nil;

wc.LPSZCLASSNAME:= "Room Example";

REGISTERCLASSEX(wc);

//создание окна

PWND := CREATEWINDOWEX(WS_EX_APPWINDOW, "Room Example", "Delphi DIRECTX Game", WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX, 0, 0, 250, 130, 0, 0, Hinstance, nil);

Init; //инициализация графики

SHOWWINDOW(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 else

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

end.

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

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

interface uses windows, Direct3D9, //модуль основных функций Direct3D

D3DX9, //модуль разширенных функций Direct3D

DIRECTINPUT, //модуль для работы с устройствами ввода

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

DXGOBJECT, //Модуль "движка" объекта dxfunc; //Модуль инициализации графики const NUM_OBJECTS = 20; //максимальное колличество объектов

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

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

// Input variable

PDEVICE:IDIRECT3DDEVICE9; //устройтво Direct3D objects:array [0..NUM_OBJECTS] of CDXGOBJECT; //массив игровых

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

//инициализации объектов procedure INITGRAPHOBJECTS(); //функция установки параметров вывода

//объектов function KEYCONTROL():boolean; //Функция для обработки данных, //полученных с устройств ввода procedure MOVECAMERA(FOFFSET:real; VECMODIFIER:TD3DXVECTOR3); //Функция перемещения камеры procedure ROTATECAMERA(FOFFSET:real; Axis:integer); //Функция поворота

//камеры public constructor CGRAPHENGINE(PD3DDEVICE:IDIRECT3DDEVICE9; HWND: HWND); overload;

destructor CGRAPHENGINE(); overload;

procedure RENDERSCENE(); //Функция отображения сцены end;

var

LPDI8: IDIRECTINPUT8 = nil; //главное устройтво DIRECTINPUT LPDIKEYBOARD: IDIRECTINPUTDEVICE8 = nil; //клавиатура

LPDIMOUSE: IDIRECTINPUTDEVICE8 = nil; //мышь

WORLDMAT, XRMAT, YRMAT, MVMAT:D3DMATRIX; //матрици для преобразования pos, VECLR, VECFB, up: TD3DXVECTOR3; //вектора направления осмотра procedure INITDIRECTINPUT( HWND: HWND ); //функция инициализации

//устройств ввода procedure RELEASEDIRECTINPUT(); //Функция освобождения захваченых DIRECTINPUT ресурсов implementation constructor CGRAPHENGINE.CGRAPHENGINE(PD3DDEVICE:IDIRECT3DDEVICE9; HWND: HWND);

var w:D3DMATRIX;

begin

//назначаем переменной PDEVICE устройство, созданное в главной программе PDEVICE:=PD3DDEVICE;

// инициализация векторов и матриц pos := D3DXVECTOR3(0.0, 0.0, 0.0);

VECLR := D3DXVECTOR3(10.0, 0.0, 0.0);

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

up := D3DXVECTOR3(0.0, 10.0, 0.0);

D3DXMATRIXIDENTITY(XRMAT);

D3DXMATRIXIDENTITY(YRMAT);

D3DXMATRIXIDENTITY(MVMAT);

// Init view matrix w._41:=20;

w._42:=-10;

w._43:=90;

WORLDMAT:=w;

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

INITGRAPHOBJECTS();

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

INITDIRECTINPUT(HWND);

end;

destructor CGRAPHENGINE.CGRAPHENGINE();

var i:integer;

begin for i:=0 to CURRENT_OBJECTS_NUM do objects[i]:=nil; //удаляем все игровые объекты end;

procedure CGRAPHENGINE.INITGRAPHOBJECTS();

var pos,pos1,rot:D3DMATRIX;

lp,lp1,lp2:TD3DXVECTOR3;

begin

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

INITOBJECT("SO_DEFAULT_ROOM", 0);

INITOBJECT("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;

Pos._31:=0; Pos._32:=0; Pos._33:=0.1; Pos._34:=0;

Pos._41:=0; Pos._42:=0; Pos._43:=0; Pos._44:=10;

//устанавливаем объект (комнату) в позицию objects[0].SETWORLDPOS(Pos);

//позиция человека

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

Pos1._21:=0; Pos1._22:=0.1; Pos1._23:=0; Pos1._24:=0;

Pos1._31:=0; Pos1._32:=0; Pos1._33:=0.1; Pos1._34:=0;

Pos1._41:=0; Pos1._42:=0; Pos1._43:=0; Pos1._44:=1;

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

D3DXMATRIXROTATIONX(Rot, -1.5);

D3DXMATRIXMULTIPLY(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);

objects[1].SETLIGHTPOS(lp1, 1);

//objects[0].SETLIGHTPOS(lp2, 2);

end;

function CGRAPHENGINE.INITOBJECT(typ:string;index:integer):boolean;

begin objects[index]:=CDXGOBJECT.CDXGOBJECT(PDEVICE);

if typ="SO_DEFAULT_ROOM" then objects[index].LOADMESHFROMFILE("media

oom.x", "media\doski.bmp");

if typ="SO_DX_MAN"then objects[index].LOADMESHFROMFILE("media\tiny.x", "");

result:=true;

end;

procedure CGRAPHENGINE.RENDERSCENE();

var i:integer;

begin

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

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

//Отображаем сцену

PDEVICE.SETRENDERSTATE(D3DRS_FILLMODE, D3DFILL_SOLID);

PDEVICE.SETSAMPLERSTATE(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

PDEVICE.SETSAMPLERSTATE(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

//Очищаем переменную устройства Direct3D

PDEVICE.Clear(0, nil, D3DCLEAR_STENCIL, D3DCOLOR_XRGB(255,255,255), 1.0, 0);

//Отображаем объекты на экране for i:=0 to CURRENT_OBJECTS_NUM-1 do objects[i].Render(0);

end;

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

//DIRECTINPUT////////

////////////////////////// procedure INITDIRECTINPUT( HWND: HWND );

var dipropdw: TDIPROPDWORD; // Структура для задания характеристик мыши begin

// Создаем главный объект DIRECTINPUT if FAILED(DIRECTINPUT8Create(GETMODULEHANDLE(nil), DIRECTINPUT_VERSION, IID_IDIRECTINPUT8, LPDI8, nil ) ) then

Exit;

LPDI8._ADDREF();

// Создаем объект для работы с клавиатурой if FAILED( LPDI8.CREATEDEVICE( GUID_SYSKEYBOARD, LPDIKEYBOARD, nil ) ) then

Exit;

LPDIKEYBOARD._ADDREF();

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

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

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

//самому if FAILED( LPDIKEYBOARD.SETDATAFORMAT( c_DFDIKEYBOARD ) ) then

Exit;

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

//DIRECTX SDK if FAILED(LPDIKEYBOARD.SETCOOPERATIVELEVEL(HWND, DISCL_BACKGROUND or DISCL_NONEXCLUSIVE ) ) then

Exit;

// Захвытываем клавиатуру

LPDIKEYBOARD.Acquire();

// Создаем объект для работы с мышью if FAILED( LPDI8.CREATEDEVICE(GUID_SYSMOUSE, LPDIMOUSE, nil)) then

Exit;

LPDIMOUSE._ADDREF();

// Устанавлаваем предопределенный формат данных if FAILED( LPDIMOUSE.SETDATAFORMAT(c_DFDIMOUSE)) then

Exit;

// Устанавливаем уровень кооперации для мыши if FAILED(LPDIMOUSE.SETCOOPERATIVELEVEL(HWND, DISCL_FOREGROUND or DISCL_EXCLUSIVE)) then

Exit;

// Захвытываем мышь

LPDIMOUSE.Acquire();

end;

procedure RELEASEDIRECTINPUT();

begin

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

LPDIKEYBOARD.Unacquire(); // Освобождаем устройство

LPDIKEYBOARD._Release();

LPDIKEYBOARD := nil;

end;

// Удаляем объект для работы с мышью if LPDIMOUSE nil then begin

LPDIMOUSE.Unacquire();

LPDIMOUSE._Release();

LPDIMOUSE := nil;

end;

// Последним удаляем главный объект DIRECTINPUT if LPDI8 nil then begin

LPDI8._Release();

LPDI8 := nil;

end;

end;

function CGRAPHENGINE.KEYCONTROL():boolean;

var View:D3DMATRIX; //матрица положения камеры

BKEYBUFFER: array [0..255] of Byte; //буфер-массив клавиш клавиатуры ms: TDIMOUSESTATE; //переменная для данных, полученных от мыши begin

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

Result := FALSE;

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

//массив

LPDIKEYBOARD.GETDEVICESTATE( SIZEOF( BKEYBUFFER ), @BKEYBUFFER );

//Если нажата клавиша Esc выход из программы if BKEYBUFFER[DIK_ESCAPE] = $080 then POSTQUITMESSAGE(0);

// Изменяем положение камеры в зависимости от нажатой клавиши if BKEYBUFFER[DIK_W] = $080 then MOVECAMERA(0.05, VECFB);

if BKEYBUFFER[DIK_S] = $080 then MOVECAMERA(-0.05, VECFB);

if BKEYBUFFER[DIK_A] = $080 then MOVECAMERA(0.05, VECLR);

if BKEYBUFFER[DIK_D] = $080 then MOVECAMERA(-0.05, VECLR);

if BKEYBUFFER[DIK_left] = $080 then ROTATECAMERA(-0.05, 0);

if BKEYBUFFER[DIK_RIGHT] = $080 then ROTATECAMERA(0.05, 0);

if BKEYBUFFER[DIK_UP] = $080 then ROTATECAMERA(0.05, 1);

if BKEYBUFFER[DIK_DOWN] = $080 then ROTATECAMERA(-0.05, 1);

//производим опрос состояния мыши

LPDIMOUSE.GETDEVICESTATE(SIZEOF(TDIMOUSESTATE), @ms);

//поворачиваем камеру в зависимости от движений мыши if ms.LX<0 then ROTATECAMERA(-0.05, 0) else if ms.LX>0 then ROTATECAMERA(0.05, 0);

if ms.LY<0 then ROTATECAMERA(0.05, 1) else if ms.LY>0 then ROTATECAMERA(-0.05, 1);

if ms.RGBBUTTONS[0] = $080 then begin end;

if ms.RGBBUTTONS[1] = $080 then begin end;

if ms.RGBBUTTONS[2] = $080 then begin end;

if ms.RGBBUTTONS[3] = $080 then begin end;

// устанавливаем векторы положения pos := D3DXVECTOR3(0.0, 0.0, 0.0);

VECLR := D3DXVECTOR3(10.0, 0.0, 0.0);

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

up := D3DXVECTOR3(0.0, 10.0, 0.0);

//перемножаем матрици

View:=WORLDMAT;

D3DXMATRIXMULTIPLY(View, XRMAT, YRMAT);

D3DXMATRIXMULTIPLY(View, View, MVMAT);

//Трансформируем координаты

D3DXVEC3TRANSFORMCOORD(pos, pos, View);

D3DXVEC3TRANSFORMCOORD(VECFB, VECFB, View);

D3DXVEC3TRANSFORMCOORD(VECLR, VECLR, View);

D3DXVEC3TRANSFORMNORMAL(up, up, View);

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

D3DXMATRIXLOOKATLH(View, pos, VECFB, up);

PDEVICE.SETTRANSFORM(D3DTS_VIEW, View);

//Включаем освещение

PDEVICE.SETRENDERSTATE(D3DRS_LIGHTING, DWORD(true));

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

end;

procedure CGRAPHENGINE.MOVECAMERA(FOFFSET:real;

VECMODIFIER:TD3DXVECTOR3);

var 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;

procedure CGRAPHENGINE.ROTATECAMERA(FOFFSET:real; Axis:integer);

var TVIEW:TD3DXMATRIX;

begin case (Axis) of

0:begin

D3DXMATRIXROTATIONY(TVIEW, FOFFSET); //поворачиваем камеру на

//коэффициент FOFFSET

D3DXMATRIXMULTIPLY(YRMAT, YRMAT, TVIEW); //устанавливаем камеру в

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

end;

1:begin

D3DXMATRIXROTATIONX(TVIEW, FOFFSET);

D3DXMATRIXMULTIPLY(XRMAT, XRMAT, TVIEW); //устанавливаем камеру в

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

end;

end;

end;

end.

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

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

interface uses windows, messages, Direct3D9, //модуль основных функций Direct3D

D3DX9, //модуль разширенных функций Direct3D

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

//(используется при установки освещения) type asingle= array [0..2] of single;

CDXGOBJECT = class //создаем класс движка объекта

// 3D Устройство

PDEVICE:IDIRECT3DDEVICE9;

// Сетка Mesh

DWNUMMATERIALS:DWORD; //номер материала

PMESH:ID3DXMESH; //сетка

PMESHTEXTURES:PAIDIRECT3DTEXTURE9; //Текстура

PMESHMATERIALS:PAD3DMATERIAL9; //материал

PTEXTURE:array [0..MAX_OBJECT_EFFECTS] of IDIRECT3DTEXTURE9; //массив

//текстур

Light:array [0..MAX_OBJECT_EFFECTS] of D3DLIGHT9; //массив

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

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

MATWORLD:D3DMATRIX;

constructor CDXGOBJECT(PD3DDEVICE:IDIRECT3DDEVICE9);

procedure LOADMESHFROMFILE(filename:string; texturename:string); //Процедура Загрузки Х-файла procedure LOADTEXTURE(filename:PANSICHAR; index:integer); //Процедура

//Загрузки текстуры function GETWORLDPOS():D3DMATRIX ; //Функция получения позиции

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

//положения объекта procedure SETLIGHTPOS(Pos:TD3DXVECTOR3; index:integer); //Процедура установки источника освещения procedure Positioning(); //Процедура установки позиции объекта function Render(ef_index:DWORD=1):boolean; //Функция отображения

// объекта end;

implementation constructor CDXGOBJECT.CDXGOBJECT(PD3DDEVICE:IDIRECT3DDEVICE9);

begin

PDEVICE:=PD3DDEVICE; //получаем образец устройства Direct3D

D3DXMATRIXIDENTITY(MATWORLD); //инициализируем мировую матрицу end;

procedure CDXGOBJECT.LOADTEXTURE(filename:PANSICHAR; index:integer);

begin

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

D3DXCREATETEXTUREFROMFILEEX(PDEVICE, filename, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE or D3DX_FILTER_MIRROR, D3DX_FILTER_TRIANGLE or D3DX_FILTER_MIRROR, 0, nil, nil, PTEXTURE[index]);

end;

procedure CDXGOBJECT.SETLIGHTPOS(Pos:TD3DXVECTOR3; index:integer);

begin

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

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

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

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

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

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

PDEVICE.SETLIGHT(index, light[index]);

PDEVICE.LIGHTENABLE(index, true);

end;

function CDXGOBJECT.GETWORLDPOS():D3DMATRIX;

begin result:=MATWORLD; //получаем мировую матрицу end;

procedure CDXGOBJECT.SETWORLDPOS(matrix:D3DMATRIX);

begin

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

procedure CDXGOBJECT.Positioning();

begin

PDEVICE.SETTRANSFORM(D3DTS_WORLD, GETWORLDPOS()); //Устанавливаем

//матрицу позиции объекта end;

function CDXGOBJECT.Render(ef_index:DWORD):boolean;

var i: integer;

begin

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

Result:= false;

Positioning();

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

PDEVICE.SETMATERIAL(PMESHMATERIALS[i]); //устанавливаем материал if PMESHTEXTURES[i]nil then //устанавливаем текстуру, если она есть PDEVICE.SETTEXTURE(0, PMESHTEXTURES[i]);

PMESH.DRAWSUBSET(i); //Рисуем сетку end;

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

end;

procedure CDXGOBJECT.LOADMESHFROMFILE(filename:string; texturename:string);

begin

DWNUMMATERIALS := LOADMESH(filename, PDEVICE, PMESH, PMESHTEXTURES, texturename, PMESHMATERIALS); //загрузка Х-файла end;

end.

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

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

interface uses Direct3D9, D3DX9, windows, SYSUTILS;

type

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

PAIDIRECT3DTEXTURE9=^AIDIRECT3DTEXTURE9;

AIDIRECT3DTEXTURE9=array[0..0] of IDIRECT3DTEXTURE9;

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

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

PAD3DMATERIAL9 = ^AD3DMATERIAL9;

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

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

PAD3DXMATERIAL = ^AD3DXMATERIAL;

//функция инициализации Direct3D function DX3DINIT(var PPID3D9: IDIRECT3D9; //переменные для работы с DIRECTX var PPID3DDEVICE9: IDIRECT3DDEVICE9;

HWND: THANDLE; //Переменная, для хранения параметров окна

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

//Функция загрузки Х-файла function LOADMESH(filename:String; //Имя Х-файла

PPID3DDEVICE9:IDIRECT3DDEVICE9; //Переменная для работы с DIRECTX var PPMESH:ID3DXMESH; //Сетка var PMESHTEXTURES: PAIDIRECT3DTEXTURE9; //Текстура texturefilename: String; //Имя файла bmp текстуры var PMESHMATERIALS: PAD3DMATERIAL9):DWORD; //Материал

//Функция загрузки текстуры function LOADTEXTURE( text_buf:PBYTEARRAY;

IWIDTH, IHEIGHT:Integer;

filename:String):Boolean;

implementation function DX3DINIT(var PPID3D9: IDIRECT3D9;

var PPID3DDEVICE9: IDIRECT3DDEVICE9;

HWND: THANDLE;

IWIDTH, IHEIGHT: Integer;

var BFULLSCREEN: Boolean):Boolean;

var d3dpp:TD3DPRESENTPARAMETERS;

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

//запрос отображения

WNDRECT, CLIENTRECT: TRECT;

d3ddm : TD3DDISPLAYMODE;

Flags: DWORD; //дополнительный флаг

HRES : HRESULT;

Aspect : real; //Номер адаптера

MATPROJECTION : TD3DMATRIX; //Матрица проекции begin

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

Result:= false;

PPID3D9:=Direct3DCREATE9(D3D_SDK_VERSION); //создаем устройство Direct3D if PPID3D9=nil then //есди устройство не созданно, то завершаем работу

//функции exit;

// Заполняем основные параметры представления

ZEROMEMORY(@d3dpp, sizeof(d3dpp));

d3dpp.BACKBUFFERWIDTH := IWIDTH; //ширина заднего буфера d3dpp.BACKBUFFERHEIGHT := IHEIGHT; //Высота заднего буфера d3dpp.AUTODEPTHSTENCILFORMAT := D3DFMT_D16;

d3dpp.ENABLEAUTODEPTHSTENCIL := TRUE;

// Запрос на отображение в полноэкранном режиме if (BFULLSCREEN=false) then

IRES:=MESSAGEBOX(HWND, "Use fullscreen mode?", "Screen", MB_YESNO or MB_ICONQUESTION) else

IRES := IDYES;

if(IRES = IDYES) then begin

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

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

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

// Установка параметров полноэкранного режима d3dpp.BACKBUFFERFORMAT := D3DFMT_R5G6B5;

d3dpp.SWAPEFFECT := D3DSWAPEFFECT_FLIP;

d3dpp.Windowed := FALSE;

d3dpp.FULLSCREEN_REFRESHRATEINHZ := D3DPRESENT_RATE_DEFAULT;

d3dpp.PRESENTATIONINTERVAL := D3DPRESENT_INTERVAL_DEFAULT;

end else begin

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

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

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

GETWINDOWRECT(HWND, WNDRECT);

GETCLIENTRECT(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;

end;

Flags := D3DCREATE_MIXED_VERTEXPROCESSING;

// Создать 3D устройство

HRES := PPID3D9.CREATEDEVICE(

D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, HWND, Flags, @d3dpp, PPID3DDEVICE9);

if (FAILED(HRES)) then exit;

// Установить перспективу

Aspect := d3dpp.BACKBUFFERWIDTH / d3dpp.BACKBUFFERHEIGHT;

D3DXMATRIXPERSPECTIVEFOVLH(MATPROJECTION, D3DX_PI/4.0, Aspect, 0.1, 2000.0);

PPID3DDEVICE9.SETTRANSFORM(D3DTS_PROJECTION, MATPROJECTION);

//Отключить освещение

PPID3DDEVICE9.SETRENDERSTATE(D3DRS_LIGHTING, DWORD(FALSE));

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

Result:=true;

end;

function LOADMESH(filename:String;

PPID3DDEVICE9:IDIRECT3DDEVICE9;

var PPMESH:ID3DXMESH;

var PMESHTEXTURES: PAIDIRECT3DTEXTURE9;

texturefilename: String;

var PMESHMATERIALS: PAD3DMATERIAL9):DWORD;

var

PD3DXMTRLBUFFER: ID3DXBUFFER;

DWNUMMATERIALS: DWORD;

d3DXMATERIALS: PAD3DXMATERIAL;

i:Integer;

begin

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

D3DXLOADMESHFROMX(PANSICHAR(filename), D3DXMESH_SYSTEMMEM, PPID3DDEVICE9, nil, @PD3DXMTRLBUFFER, nil, @DWNUMMATERIALS, PPMESH);

d3DXMATERIALS := PD3DXMTRLBUFFER.GETBUFFERPOINTER();

// Инициализируем материалы и текстуры

GETMEM(PMESHMATERIALS, SIZEOF(TD3DMATERIAL9)*DWNUMMATERIALS);

GETMEM(PMESHTEXTURES, SIZEOF(IDIRECT3DTEXTURE9)*DWNUMMATERIALS);

for i:=0 to DWNUMMATERIALS-1 do begin

// Копируем материал

PMESHMATERIALS[i] := d3DXMATERIALS[i].MATD3D;

// Создаем текстуру, если она есть if (FAILED(D3DXCREATETEXTUREFROMFILE(PPID3DDEVICE9, d3DXMATERIALS[i].PTEXTUREFILENAME, PMESHTEXTURES[i]))=true) then if (FAILED(D3DXCREATETEXTUREFROMFILE(PPID3DDEVICE9, PANSICHAR(texturefilename), PMESHTEXTURES[i]))) then

PMESHTEXTURES[i] := nil;

end;

Result:=DWNUMMATERIALS;

end;

function LOADTEXTURE(text_buf:PBYTEARRAY;IWIDTH, IHEIGHT:Integer; filename:String):Boolean;

var

HFILE:THANDLE;

bmpfilehdr:BITMAPFILEHEADER;

DWREAD:DWORD;

ptr:String;

bmpinfohdr:BITMAPINFOHEADER;

IIMAGESIZE, bytesgiven, i, p:Integer;

buf:array of byte;

BTSURF:PBYTE;

imagebits:PBYTEARRAY;

WDSURF: PWORD;

TLIMAGE:PRGBTRIPLE;

begin

Result:=false;

HFILE := CREATEFILE(PANSICHAR(filename), GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

if (HFILE = INVALID_HANDLE_VALUE) then exit;

if (READFILE(HFILE, bmpfilehdr, sizeof(bmpfilehdr), DWREAD, nil)=false) then exit;

ptr:=Chr(bmpfilehdr.BFTYPE);

if (ptr[1]"B") then exit;

// Проверяем заголовок изображения if(READFILE(HFILE, bmpinfohdr, sizeof(bmpinfohdr), DWREAD, nil)=false) then exit;

if (bmpinfohdr.BICOMPRESSIONBI_RGB) then exit;

IIMAGESIZE:=bmpinfohdr.BISIZEIMAGE;

if (IIMAGESIZE=0) then

IIMAGESIZE:=IWIDTH*bmpinfohdr.BIBITCOUNT*IHEIGHT;

// Читаем данные изображения в буфер

SETLENGTH(buf,IIMAGESIZE);

if (READFILE(HFILE, buf[0], IIMAGESIZE, DWREAD, nil)=false) then exit;

if (bmpinfohdr.BIBITCOUNT24)then exit;

bytesgiven:=(IWIDTH*3 3) and (not 3);

BTSURF := PBYTE(text_buf);

imagebits := @buf[(IHEIGHT-1)*bytesgiven];

for i:=0 to IHEIGHT-1 do begin

WDSURF:=PWORD(BTSURF);

TLIMAGE:=PRGBTRIPLE(imagebits);

for p:=0 to IWIDTH-1 do begin

PDWORD(WDSURF)^ := (TLIMAGE.RGBTBLUE)

(TLIMAGE.RGBTGREEN SHL 8)

(TLIMAGE.RGBTRED SHL 16)

$FF000000;

TLIMAGE:=Pointer(Integer(TLIMAGE) 3);

WDSURF:=Pointer(Integer(WDSURF) 4);

end;

BTSURF := Pointer(Integer(BTSURF) IWIDTH*4);

imagebits := Pointer(Integer(imagebits)-bytesgiven);

end;

Result:=true;

end;

end.

Размещено на

Вы можете ЗАГРУЗИТЬ и ПОВЫСИТЬ уникальность
своей работы


Новые загруженные работы

Дисциплины научных работ





Хотите, перезвоним вам?