
- •Джим Адамс
- •2-е издание
- •Оглавление
- •Благодарности
- •Об авторе
- •Введение
- •О чем эта книга
- •Для кого предназначена книга
- •Организация книги
- •Содержимое CD-ROM
- •Типографские соглашения
- •Что нужно для начала
- •Глава 1 Подготовка к работе с книгой
- •Глава 1 Подготовка к работе с книгой
- •DirectX
- •Подготовка DirectX
- •Отладочные и дистрибутивные версии
- •Установка DirectX 9
- •Установка DirectMusic Producer
- •Включаемые файлы и библиотеки
- •Подготовка компилятора
- •Указание каталогов для DirectX
- •Связывание с библиотеками
- •Установка поведения по умолчанию для char
- •Окончательные и отладочные версии
- •Многопоточные библиотеки
- •Программирование для Windows
- •Потоки и многопоточность
- •Критические секции
- •Использование COM
- •Инициализация COM
- •IUnknown
- •Инициализация и освобождение объектов
- •Запрос интерфейсов
- •Поток выполнения программы
- •Модульное программирование
- •Состояния и процессы
- •Состояния приложения
- •Процессы
- •Обработка данных приложения
- •Использование упаковки данных
- •Тестирование системы упаковки данных
- •Построение каркаса приложения
- •Структурирование проекта
- •Завершаем подготовку
- •Глава 2 Рисование с DirectX Graphics
- •Глава 3 Взаимодействие с пользователем с DirectInput
- •Глава 4 Воспроизведение звуков и музыки с DirectX Audio и DirectShow
- •Глава 2 Рисование с DirectX Graphics
- •Сердце трехмерной графики
- •Системы координат
- •Конструирование объектов
- •Списки, полосы и веера
- •Порядок вершин
- •Окрашивание полигонов
- •Преобразования
- •Начинаем работать с DirectX Graphics
- •Компоненты Direct3D
- •Инициализация системы
- •Получение интерфейса Direct3D
- •Выбор видеорежима
- •Установка метода показа
- •Создание интерфейса устройства и инициализация дисплея
- •Потеря устройства
- •Знакомство с D3DX
- •Трехмерная математика
- •Матричная алгебра
- •Конструирование матриц
- •Комбинирование матриц
- •Переход от локальных координат к координатам вида
- •Переходим к рисованию
- •Использование вершин
- •Настраиваемый формат вершин
- •Использование буфера вершин
- •Создание буфера вершин
- •Блокировка буфера вершин
- •Заполнение данных вершин
- •Поток вершин
- •Вершинные шейдеры
- •Преобразования
- •Мировое преобразование
- •Преобразование вида
- •Преобразование проекции
- •Материалы и цвета
- •Очистка порта просмотра
- •Начало и завершение сцены
- •Визуализация полигонов
- •Показ сцены
- •Использование наложения текстур
- •Использование наложения текстур в Direct3D
- •Загрузка текстуры
- •Установка текстуры
- •Использование выборок
- •Визуализация текстурированных объектов
- •Альфа-смешивание
- •Включение альфа-смешивания
- •Рисование с альфа-смешиванием
- •Копирование с учетом прозрачности и альфа-проверка
- •Загрузка текстур с цветовым ключом
- •Включение альфа-проверки
- •Пример копирования с учетом прозрачности
- •Освещение
- •Использование точечного света
- •Использование зонального света
- •Использование направленного света
- •Фоновый свет
- •Установка света
- •Использование нормалей
- •Да будет свет!
- •Использование шрифтов
- •Создание шрифта
- •Рисование текста
- •Щиты
- •Частицы
- •Сортировка по глубине и Z-буферизация
- •Работа с портом просмотра
- •Работа с сетками
- •X-файлы
- •Формат X-файлов
- •Использование иерархии фреймов
- •Создание X-файлов с сетками
- •Разбор X-файлов
- •Сетки в библиотеке D3DX
- •Объект ID3DXBuffer
- •Стандартные сетки
- •Визуализация сетки
- •Скелетные сетки
- •Загрузка скелетных сеток
- •Обновление и визуализация скелетной сетки
- •X-стиль трехмерной анимации
- •Техника ключевых кадров
- •Анимация в X-файлах
- •Заканчиваем с графикой
- •Знакомство с устройствами ввода
- •Взаимодействие через клавиатуру
- •Работа с клавиатурой в Windows
- •Играем с мышью
- •Удовольствие с джойстиком
- •Использование DirectInput
- •Представляем основы DirectInput
- •Инициализация DirectInput
- •Использование устройств DirectInput
- •Получение GUID устройства
- •Создание COM-объекта устройства
- •Установка формата данных
- •Установка уровня кооперации
- •Установка специальных свойств
- •Захват устройства
- •Опрос устройства
- •Чтение данных
- •DirectInput и клавиатура
- •DirectInput и мышь
- •DirectInput и джойстик
- •Заканчиваем с вводом
- •Глава 4 Воспроизведение звуков и музыки с DirectX Audio и DirectShow
- •Основы звука
- •Цифровая звукозапись
- •Музыкальное безумие
- •MIDI
- •DirectMusic
- •Знакомство с DirectX Audio
- •Использование DirectSound
- •Инициализация DirectSound
- •Установка уровня кооперации
- •Установка формата воспроизведения
- •Создание объекта первичного звукового буфера
- •Установка формата
- •Запуск первичного звукового буфера
- •Использование вторичных звуковых буферов
- •Блокируй и загружай — загрузка звуковых данных в буфер
- •Воспроизведение звукового буфера
- •Изменение громкости, позиционирования и частоты
- •Регулировка громкости
- •Позиционирование
- •Изменение частоты
- •Потеря фокуса
- •Использование уведомлений
- •Использование потоков для сканирования событий
- •Загрузка звука в буфер
- •Потоковое воспроизведение звука
- •Работа с DirectMusic
- •Начинаем работу с DirectMusic
- •Создание объекта исполнителя
- •Создание объекта загрузчика
- •Работа с музыкальными сегментами
- •Загрузка музыкальных сегментов
- •Загрузка инструментов
- •Настройки для MIDI
- •Установка инструментов
- •Использование циклов и повторов
- •Воспроизведение и остановка сегмента
- •Выгрузка данных сегмента
- •Изменение музыки
- •Установка громкости
- •Смена темпа
- •Захват аудио-канала
- •Присоединяемся к MP3-революции
- •Использование DirectShow
- •Загрузка медиа-файла
- •Управление воспроизведением вашей музыки и звуков
- •Воспроизведение звука
- •Приостановка воспроизведения
- •Завершение воспроизведения
- •Обнаружение событий
- •Освобождение ресурсов DirectShow
- •Заканчиваем с музыкой
- •Глава 5 Работа с сетью с DirectPlay
- •Знакомство с сетями
- •Сетевые модели
- •Лобби
- •Задержка и запаздывание
- •Коммуникационные протоколы
- •Адресация
- •Введение в DirectPlay
- •Сетевые объекты
- •Работа с игроками
- •Сетевые сообщения
- •Асинхронная и синхронная работа
- •Безопасность
- •Гарантированная доставка
- •Регулировка потока
- •От маленьких байтов к большим словам
- •Идентификация приложений по GUID
- •Инициализация сетевого объекта
- •Использование адресов
- •Инициализация объекта адреса
- •Добавление компонентов
- •Установка поставщика услуг
- •Выбор порта
- •Назначение устройства
- •Использование обработчиков сообщений
- •Конфигурирование данных сессии
- •Данные сессии сервера
- •Данные сессии клиента
- •Работа с сервером
- •Поддержка игроков
- •Работа с сообщениями о создании игрока
- •Получение имени игрока
- •Уничтожение игроков
- •Получение данных
- •Отправка сообщений сервера
- •Завершение сессии на главном узле
- •Работа с клиентом
- •Получение и отправка сообщений
- •Получение идентификатора игрока
- •Завершение сессии клиента
- •Заканчиваем с сетями
- •Глава 6 Создаем ядро игры
- •Знакомство с концепцией ядра
- •Системное ядро
- •Использование объекта ядра cApplication
- •Обработка состояний с cStateManager
- •Процессы и cProcessManager
- •Управление данными с cDataPackage
- •Графическое ядро
- •Графическая система и cGraphics
- •Изображения и cTexture
- •Цвета и cMaterial
- •Освещение с cLight
- •Текст и шрифты с использованием cFont
- •Вершины и cVertexBuffer
- •Мировое преобразование с cWorldPosition
- •Преобразование вида и cCamera
- •Загружаемые сетки и использование cMesh
- •Рисуем объекты, используя cObject
- •Делаем сетки двигающимися с cAnimation
- •Ядро ввода
- •Использование DirectInput с cInput
- •Устройства ввода и cInputDevice
- •Использование ядра ввода
- •Звуковое ядро
- •Управление DirectX Audio с cSound
- •Использование волновых данных и cSoundData
- •Воспроизведение звука с cSoundChannel
- •Слушаем музыку с cMusicChannel
- •Смешивание инструментов с cDLS
- •Воспроизведение MP3 с cMP3
- •Сетевое ядро
- •Запрос адаптера с cNetworkAdapter
- •Серверы и cNetworkServer
- •Работа клиента и cNetworkClient
- •Заканчиваем изучать ядро игры
- •Часть III Программирование ролевых игр
- •Глава 7 Использование двухмерной графики
- •Глава 8 Создание трехмерного графического движка
- •Глава 9 Объединение двухмерной и трехмерной графики
- •Глава 10 Реализация скриптов
- •Глава 11 Определение и использование объектов
- •Глава 12 Управление игроками и персонажами
- •Глава 13 Работа с картами и уровнями
- •Глава 14 Создание боевых последовательностей
- •Глава 15 Сетевой режим для многопользовательской игры
- •Глава 7 Использование двухмерной графики
- •Знакомство с блоками и картами
- •Использование блоков с DirectX
- •Построение класса для работы с блоками
- •cTiles::Create
- •cTiles::Free
- •cTiles::Load
- •cTiles::Free
- •cTiles::GetWidth, cTiles::GetHeight и cTiles::GetNum
- •cTiles::SetTransparent
- •cTiles::Draw
- •Использование класса работы с блоками
- •Основы работы с блочной графикой
- •Рисование основной карты
- •Использование нескольких слоев
- •Добавление объектов
- •Плавная прокрутка
- •Карта и мышь
- •Создание класса карты
- •Псевдотрехмерные блоки
- •Работа с большими растрами
- •Создание больших блоков
- •Большой пример
- •Заканчиваем с двухмерной графикой
- •Сетки в качестве уровней
- •Загрузка уровней
- •Рисование комнат
- •Усовершенствование базовой техники
- •Знакомство с пирамидой видимого пространства
- •Плоскости и отсечение
- •Проверка видимости с плоскостями
- •Проверка всей пирамиды
- •Класс cFrustum
- •cFrustum::Construct
- •cFrustum::CheckPoint, CheckCube, CheckRectangle и CheckSphere
- •Разработка профессионального трехмерного движка
- •Знакомство с движком NodeTree
- •Создание узлов и деревьев
- •Сканирование и рисование дерева
- •Работа с группами материалов
- •Создание класса cNodeTreeMesh
- •cNodeTreeMesh::Create и cNodeTreeMesh::Free
- •cNodeTreeMesh::SortNode
- •cNodeTreeMesh::AddNode
- •cNodeTreeMesh::Render
- •Использование cNodeTreeMesh
- •Добавление к миру трехмерных объектов
- •Вычисление ограничивающей сферы
- •Ограничивающие сферы и пирамида видимого пространства
- •Обнаружение столкновений сеток
- •Столкновение с миром
- •Испускание лучей
- •Блокировка пути
- •Перемещение вверх и вниз
- •Быстрая проверка пересечения
- •Обнаружение столкновений в классе cNodeTreeMesh
- •Когда сталкиваются сетки
- •Щелчки мыши и сетки
- •Использование небесного куба
- •Создание класса небесного куба
- •cSkyBox::Create и cSkyBox::Free
- •cSkyBox::LoadTexture
- •cSkyBox::Rotate и cSkyBox::RotateRel
- •cSkyBox::Render
- •Использование класса небесного куба
- •Заканчиваем с трехмерной графикой
- •Глава 9 Объединение двухмерной и трехмерной графики
- •Смешивание двух измерений
- •Использование двухмерных объектов в трехмерном мире
- •Рисование блоков в трехмерном мире
- •Загрузка сетки уровня
- •Загрузка блоков
- •Подготовка к рисованию
- •Рисование сетки уровня
- •Рисование двухмерных объектов
- •Перемещение в трехмерном мире
- •Добавление трехмерных объектов к двухмерному миру
- •Работа с двухмерным фоном
- •Работа с сеткой сцены
- •Визуализация сцены
- •Добавление трехмерных объектов
- •Столкновения и пересечения
- •Заканчиваем смешивание графики
- •Глава 10 Реализация скриптов
- •Что такое скрипты
- •Создание системы Mad Lib Script
- •Проектирование системы Mad Lib Script
- •Программирование системы Mad Lib Script
- •Работа с шаблонами действий
- •Создание элементов скрипта
- •Объединяем все в классе cActionTemplate
- •Работа с редактором MLS Editor
- •Выполнение скриптов Mad Lib
- •Применение скриптов в играх
- •Заканчиваем со скриптами
- •Определение объектов для вашей игры
- •Форма объекта
- •Определение функции объекта
- •Оружие
- •Броня
- •Аксессуары
- •Коллекции
- •Транспорт
- •Прочее
- •Добавление функций к объекту
- •Категории предметов и значения
- •Задание цены предметов
- •Флаги предмета
- •Ограничения использования
- •Присоединение скриптов к предметам
- •Сетки и изображения
- •Итоговая структура данных предмета
- •Главный список предметов
- •Конструирование MIL
- •Использование редактора MIL
- •Доступ к предметам из MIL
- •Управление предметами с помощью системы контроля имущества
- •Разработка ICS карты
- •cMapICS::Load, cMapICS::Save и cMapICS::Free
- •cMapICS::Add и cMapICS::Remove
- •cMapICS::GetNumItems, cMapICS::GetParentItem и cMapICS::GetItem
- •Использование класса cMapICS
- •Разработка ICS персонажа
- •Определение класса cCharICS
- •Использование класса cCharICS
- •Заканчиваем с объектами и имуществом
- •Глава 12 Управление игроками и персонажами
- •Игроки, персонажи, монстры — о боже!
- •Определение персонажей в вашей игре
- •Способности персонажа
- •Атрибуты персонажа
- •Дополнительные статусы персонажа
- •Классы персонажей
- •Действия персонажей
- •Персонажи игроков
- •Передвижение игрока
- •Управление ресурсами
- •Увеличение опыта и силы
- •Независимые персонажи
- •Персонажи монстров
- •Графика персонажей
- •Перемещение персонажей
- •Управление персонажем игрока
- •Управление направлением
- •Управление поворотом
- •Управление от первого лица
- •Управление независимыми персонажами
- •Ожидание
- •Блуждание по области
- •Ходьба по маршруту
- •Использование маршрутных точек
- •Ходьба от точки к точке
- •Быстрее Пифагора
- •Прохождение маршрута
- •Следование за другим персонажем
- •Избегание персонажа
- •Автоматическое управление персонажами
- •Общение между персонажами
- •Говорящие куклы
- •Управляемые скриптами говорящие куклы
- •Показ диалогов и другого текста
- •Класс cWindow
- •cWindow::cWindow и cWindow::~cWindow
- •cWindow::Create и cWindow::Free
- •cWindow::SetText
- •cWindow::Move
- •cWindow::GetHeight
- •cWindow::Render
- •Использование cWindow
- •Скрипты и персонажи
- •Класс скрипта
- •Создание производного класса скрипта
- •Производный класс
- •Использование производного класса
- •Управление ресурсами
- •Использование предметов
- •Использование магии
- •Торговля и обмен
- •Работа с магией и заклинаниями
- •Графика заклинания
- •Функция заклинания
- •Изменение здоровья и маны
- •Установка и снятие статусов
- •Воскрешение и мгновенная смерть
- •Расколдовывание
- •Телепортация
- •Нацеливание заклинаний, стоимость и шансы
- •Главный список заклинаний
- •Список заклинаний
- •Определение заклинаний с MSL Editor
- •Создание контроллера заклинаний
- •Сетки и sSpellMeshList
- •Отслеживание заклинаний с использованием sSpellTracker
- •Класс cSpellController
- •cSpellController::cSpellController и cSpellController::~sSpellController
- •cSpellController::Init и cSpellController::Shutdown
- •cSpellController::Free
- •cSpellController::GetSpell
- •cSpellController::Add
- •cSpellController::SetAnimData
- •cSpellController::Update
- •cSpellController::Render
- •Определение жертвы и обработка эффекта заклинания
- •Использование контроллера заклинаний
- •Сражения и персонажи
- •Использование правил сражения для атаки
- •Нанесение удара
- •Отклонение атаки
- •Обработка ущерба
- •Заклинания в битве
- •Интеллект в битвах
- •Построение главного списка персонажей
- •MCL Editor
- •Использование определений персонажей
- •Создание класса контроллера персонажей
- •Сетки и sCharacterMeshList
- •Зацикливание анимации и sCharAnimationInfo
- •Перемещение и sRoutePoint
- •Отслеживание персонажей с sCharacter
- •Класс sCharacterController
- •Использование sCharacterController
- •Демонстрация персонажей в программе Chars
- •Заканчиваем с персонажами
- •Глава 13 Работа с картами и уровнями
- •Размещение персонажей на карте
- •Список персонажей карты
- •Загрузка списка персонажей карты
- •Использование списка персонажей карты в вашей игре
- •Скриптовое размещение
- •Использование триггеров карты
- •Сферические триггеры
- •Кубический триггер
- •Цилиндрический триггер
- •Треугольный триггер
- •Срабатывание триггеров
- •Создание класса триггера
- •cTrigger::cTrigger и cTrigger::~cTrigger
- •cTrigger::Load и cTrigger::Save
- •cTrigger::AddTrigger
- •cTrigger::AddSphere, cTrigger::AddBox, cTrigger::AddCylinder и cTrigger::AddTriangle
- •cTrigger::Remove и cTrigger::Free
- •cTrigger::GetTrigger
- •cTrigger::GetEnableState и cTrigger::Enable
- •cTrigger::GetNumTriggers и cTrigger::GetParentTrigger
- •Использование триггеров
- •Определение файла триггеров
- •Загрузка файла триггеров
- •Касание триггера
- •Блокирование пути барьерами
- •cBarrier::SetMesh и cBarrier::SetAnim
- •cBarrier::Render
- •Добавление барьеров с cBarrier
- •Использование класса барьера
- •Создание файла данных барьеров
- •Загрузка данных барьеров
- •Проверка столкновений с барьерами
- •Визуализация барьеров
- •Использование автоматических карт
- •Автоматические карты в действии
- •Большие карты, малые карты
- •Загрузка и отображение автоматической карты
- •Создание класса автоматической карты
- •cAutomap::cAutomap и cAutomap::~cAutomap
- •cAutomap::Create и cAutoMap::Free
- •cAutomap::Load и cAutomap::Save
- •cAutomap::GetNumSections и cAutomap::EnableSection
- •cAutomap::SetWindow и cAutomap::Render
- •Использование cAutomap
- •Заканчиваем с картами и уровнями
- •Проектирование внешних боевых последовательностей
- •Техническая сторона
- •Разработка боевых последовательностей
- •Глобальные данные
- •cApp::cApp
- •cApp::Init
- •cApp::Shutdown
- •cApp::Frame
- •cApp::GetCharacterAt
- •Использование боевых аранжировок
- •Заканчиваем с боевыми последовательностями
- •Безумная многопользовательская сеча
- •Проектирование многопользовательской игры
- •Демонстрационная программа Network Game
- •Создание архитектуры многопользовательской игры
- •Работаем вместе: клиент и сервер
- •Взгляд на сервер
- •Взгляд на клиента
- •Работа над игровым сервером
- •Хранение информации игрока
- •Обработка сообщений
- •От сообщений DirectPlay к игровым сообщениям
- •Очередь сообщений
- •Обработка игровых сообщений
- •cApp::AddPlayer
- •cApp::RemovePlayer
- •cApp::PlayerInfo
- •cApp::PlayerStateChange
- •Обновление игроков
- •Обновление сетевых клиентов
- •Вычисление запаздывания
- •Самое трудное позади!
- •Работа над игровым клиентом
- •Обработка данных игрока
- •Сетевой компонент
- •Обработка сообщений
- •cApp::CreatePlayer
- •cApp::DestroyPlayer
- •cApp::ChangeState
- •Обновление локального игрока
- •Обновление всех игроков
- •Клиент во всем блеске
- •Заканчиваем с многопользовательскими играми
- •Часть IV Завершающие штрихи
- •Глава 16 Объединяем все вместе в законченную игру
- •Глава 16 Объединяем все вместе в законченную игру
- •Проектирование простой игры
- •Написание сценария игры
- •Цель игры The Tower
- •Разработка уровней
- •Определение персонажей
- •Распределение персонажей
- •Создание предметов и заклинаний
- •Разработка скриптов
- •Определение управления
- •Планирование хода игры
- •Программирование примера игры
- •Структурирование приложения
- •Конструктор cApp
- •Функция приложения Init
- •Функция Shutdown
- •Обработка кадров в функции Frame
- •Использование обработки на основе состояний
- •Работа с картами
- •Использование барьеров и триггеров
- •Управление персонажами
- •Поддержка торговли
- •Воспроизведение звуков и музыки
- •Визуализация сцены
- •Обработка скриптов
- •Собираем части
- •Завершаем создание игры
- •Часть V Приложения
- •Приложение A Список литературы
- •Рекомендованное чтение
- •Получение помощи в Web
- •Приложение B Содержимое CD-ROM
- •DirectX 9.0 SDK
- •Пробная версия GoldWave 5
- •Пробная версия Paint Shop Pro 8
- •gameSpace Light
- •Приложение C Словарь терминов
- •Алфавитный указатель

Джим Адамс
Таблица 8.1.(продолжение) Переменные cNodeTreeMesh
Переменная Описание
m_ParentNode m_NumGroups m_Groups m_NumPolygons m_MaxPolygons
Голова связанного списка узлов Количество групп материалов Массив групп материалов
Количество полигонов в исходной сетке
Хранит максимальное количество полигонов, допустимое для узла (при его превышении происходит разделение узла)
m_PolygonList |
Массив структур sPolygon, хранящих информацию о |
|
каждом полигоне сетки |
m_Mesh
m_VertexPtr
m_VertexFVF m_VertexSize
Указатель на исходную сетку (структуру sMesh, используемую графическим ядром)
Используется для доступа к буферу вершин исходной сетки
Дескриптор FVF исходной сетки
Хранит размер одной вершины исходной сетки (в байтах)
Продолжайте чтение главы, и я укажу, когда переменные класса
вступают в игру, а сейчас пришло время больше узнать о функциях класса cNodeTreeMesh.
cNodeTreeMesh::Create и cNodeTreeMesh::Free
Функция cNodeTreeMesh::Create — это место где начинается весь
процесс. Для создания структуры NodeTree вам необходима исходная сетка с которой мы будем работать, и лучше всего взять ее из объекта cMesh.
Объект cMesh хранит список сеток, содержащихся в одном X-файле, и мы для простоты будем иметь дело только с первой сеткой из списка.
Передав функции Create указатель на инициализированный объект cGraphics, предварительно загруженный объект cMesh и тип дерева, который вы хотите использовать (QUADTREE или OCTREE), вы
инициализируете класс, после чего он готов к визуализации.
Помимо уже упомянутых аргументов вам надо задать максимальное количество полигонов для узла и максимальный размер узла, прежде чем ему потребуется разделение (когда он содержит максимально возможное для узла количество полигонов).
BOOL cNodeTreeMesh::Create(cGraphics *Graphics, cMesh *Mesh, |
|
|
|
int TreeType, |
|
{ |
float MaxSize, long MaxPolygons) |
|
*LoadMesh; |
|
|
ID3DXMesh |
|
|
unsigned short *IndexPtr; |
|
|
|
|
|
|
netlib.narod.ru |
403 |

Глава 8. Создание трехмерного графического движка
unsigned long |
*Attributes; |
float |
MaxX, MaxY, MaxZ; |
unsigned long |
i; |
//Освобождаем предыдущую сетку
Free();
//Проверка ошибок
if((m_Graphics = Graphics) == NULL) return FALSE;
if(Mesh == NULL) return FALSE;
if(!Mesh->GetParentMesh()->m_NumMaterials) return FALSE;
// Получаем |
сведения о сетке |
|
m_Mesh |
|
= Mesh->GetParentMesh(); |
LoadMesh |
|
= m_Mesh->m_Mesh; |
m_VertexFVF |
|
= LoadMesh->GetFVF(); |
m_VertexSize |
|
= D3DXGetFVFVertexSize(m_VertexFVF); |
m_NumPolygons |
= LoadMesh->GetNumFaces(); |
|
m_MaxPolygons |
= MaxPolygons; |
|
// Создаем список полигонов и групп |
||
m_Polygons |
= |
new sPolygon[m_NumPolygons](); |
m_NumGroups |
= |
m_Mesh->m_NumMaterials; |
m_Groups |
= |
new sGroup[m_NumGroups](); |
// Блокируем буфер индексов и буфер атрибутов
LoadMesh->LockIndexBuffer(D3DLOCK_READONLY,(void**)&IndexPtr); LoadMesh->LockAttributeBuffer(D3DLOCK_READONLY, &Attributes);
Здесь мы видим кое-что новое. ID3DXMesh использует буфер вершин; кроме того, ID3DXMesh использует буфер индексов! Вы только что читали
об этом в предыдущем разделе; D3DX использует буферы вершин и индексов для экономии памяти и ускорения визуализации, поскольку буфер индексов позволяет во время визуализации несколько раз использовать одну и ту же вершину без необходимости несколько раз сохранять координаты вершины в буфере вершин.
Также у объекта ID3DXMesh есть нечто, называемое буфер атрибутов (attribute buffer), представляющий собой массив значений, определяющих какой материал какой полигон использует. Между элементами массива и полигонами существует отношение один к одному. Именно этот буфер атрибутов используется, чтобы включать полигоны в соответствующие им группы материалов.
В последующем коде начинается перебор всех полигонов сетки с извлечением трех вершин, используемых для конструирования каждого полигона. Помимо этого, вы в структуре данных полигона сохраняете материал, применяемый для рисования этого полигона.
// Загрузка данных полигонов в структуры for(i = 0; i < m_NumPolygons; i++) {
m_Polygons[i].Vertex0 = *IndexPtr++; m_Polygons[i].Vertex1 = *IndexPtr++; m_Polygons[i].Vertex2 = *IndexPtr++;
404 |
netlib.narod.ru |

Джим Адамс
m_Polygons[i].Group = Attributes[i]; m_Polygons[i].Timer = 0; m_Groups[Attributes[i]].NumPolygons++;
}
// Разблокирование буферов
LoadMesh->UnlockAttributeBuffer(); LoadMesh->UnlockIndexBuffer();
// Построение индексных буферов групп вершин for(i = 0; i < m_NumGroups; i++) {
if(m_Groups[i].NumPolygons != 0) m_Graphics->GetDeviceCOM()->CreateIndexBuffer(
m_Groups[i].NumPolygons * 3 * sizeof(unsigned short),
D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED,
&m_Groups[i].IndexBuffer, NULL);
}
В показанном выше коде массив индексов и буфер атрибутов сетки заблокированы. После этого вы просто строите список данных полигонов (какие вершины образуют полигон и какая группа материалов используется). Завершив построение списка данных полигонов, вы разблокируете буферы индексов и атрибутов и формируете фактические группы материалов.
Каждая группа материалов содержит объект IDirectIndexBuffer9
для хранения индексов вершин, которые надо рисовать. Чтобы создать этот буфер индексов вы используете функцию CreateIndexBuffer, во многом похожую на функцию CreateVertexBuffer, о которой вы
читали в главе 2. Единственное различие в том, что буфер индексов использует для значений индексов переменные типа short (2 байта), и
выделяет по три индекса на полигон. Обратите внимание, что нужно выделить достаточно индексов для каждого полигона, относящегося к каждой группе материалов.
// Получаем размер ограничивающего куба
MaxX = (float)max(fabs(Mesh->GetParentMesh()->m_Min.x), fabs(Mesh->GetParentMesh()->m_Max.x)); MaxY = (float)max(fabs(Mesh->GetParentMesh()->m_Min.y), fabs(Mesh->GetParentMesh()->m_Max.y));
MaxZ = (float)max(fabs(Mesh->GetParentMesh()->m_Min.z), fabs(Mesh->GetParentMesh()->m_Max.z));
m_Size = max(MaxX, max(MaxY, MaxZ)) * 2.0f; m_MaxSize = MaxSize;
// Создаем родительский узел m_ParentNode = new sNode();
Здесь вычисляется точка наиболее удаленная от центра исходной сетки. Для упрощения вычислений мир считается идеальным кубом, поэтому мы используем расстояние от центра сетки до внешнего края в качестве размера
мирового ограничивающего параллелепипеда. После создания родительского узла (m_ParentNode) вы блокируете буфер вершин сетки чтобы прочитать
координаты вершин. И, наконец, узлы сортируются путем вызова функции
SortNode.
netlib.narod.ru |
405 |

Глава 8. Создание трехмерного графического движка
// Сортировка полигонов в узлах
LoadMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&m_VertexPtr);
SortNode(m_ParentNode, 0.0f, 0.0f, 0.0f, m_Size); LoadMesh->UnlockVertexBuffer();
m_Timer = 0; |
// Сброс таймера |
return TRUE;
}
После сортировки буфер вершин сетки разблокируется и функция возвращает TRUE, сообщая об успешном завершении. Теперь представление
сетки в виде узлов дерева готово к использованию. Завершив работу с этим представлением вы вызываете cNodeTreeMesh::Free для освобождения
ресурсов сетки:
BOOL cNodeTreeMesh::Free() |
|
{ |
// Удаляем родительский и дочерние узлы |
delete m_ParentNode; |
|
m_ParentNode = NULL; |
|
m_NumPolygons = 0; |
// Больше нет полигонов |
delete [] m_PolygonList; // Удаляем массив полигонов |
|
m_PolygonList = NULL; |
|
m_NumGroups = 0; |
// Больше нет групп материалов |
delete [] m_Groups; |
// Удаляем группы материалов |
m_Groups = NULL; |
|
m_Graphics = NULL;
return TRUE;
}
cNodeTreeMesh::SortNode
cNodeTreeMesh::SortNode — это рекурсивная функция (вызывающая
сама себя), которая подсчитывает количество полигонов, находящихся в узле трехмерного пространства и решает надо ли разделить узел на четыре или
восемь дочерних узлов (в зависимости от типа дерева). После того, как узлы соответствующим образом разделены, функция SortNode строит список
полигонов, находящихся в узле трехмерного пространства.
Код функции SortNode начинается с проверки правильности
переданных ей аргументов. Затем функция сохраняет координаты узла и начинает разделение узлов.
ПРИМЕЧАНИЕ Если вы используете квадродерево, в операциях отсечения и разделения надо игнорировать высоту.
Обратите внимание, что в зависимости от режима в коде класса cNodeTreeMesh значения высоты игнорируются
или обрабатываются по-другому.
406 |
netlib.narod.ru |

Джим Адамс
void cNodeTreeMesh::SortNode(sNode *Node,
|
float XPos, float YPos, float ZPos, |
{ |
float Size) |
|
|
unsigned long i, Num; |
|
float |
XOff, YOff, ZOff; |
//Проверка ошибок if(Node == NULL)
return;
//Сохраняем координаты и размер узла
Node->XPos = XPos;
Node->YPos = (m_TreeType==QUADTREE)?0.0f:YPos;
Node->ZPos = ZPos;
Node->Size = Size;
// Смотрим, есть ли полигоны в узле
if(!(Num = CountPolygons(XPos, YPos, ZPos, Size))) return;
//Разделяем узел, если его размер больше максимума
//и в нем слишком много полигонов
if(Size > m_MaxSize |
&& Num |
> m_MaxPolygons) { |
|
|
|||||||
for(i=0; |
i < (unsigned |
long)((m_TreeType==QUADTREE)?4:8); i++) { |
|||||||||
XOff |
= (((i |
% 2) |
< |
1) |
? -1.0f |
: 1.0f) |
* (Size / 4.0f); |
||||
ZOff |
= |
(((i |
% |
4) |
< |
2) |
? -1.0f |
: |
1.0f) |
* |
(Size / 4.0f); |
YOff |
= |
(((i |
% |
8) |
< |
4) |
? -1.0f |
: |
1.0f) |
* |
(Size / 4.0f); |
//Смотрим, есть ли полигоны в ограничивающем
//параллелепипеде нового узла
if(CountPolygons(XPos + XOff, YPos + YOff, ZPos + ZOff, Size/2.0f)) {
//Создаем новый дочерний узел
Node->Nodes[i] = new sNode();
//Сортируем полигоны с новым дочерним узлом
SortNode(Node->Nodes[i], XPos+XOff, YPos+YOff,
ZPos+ZOff, Size/2.0f);
}
}
return;
}
К этому моменту все полигоны, находящиеся в ограничивающем параллелепипеде узла посчитаны. Если полигонов слишком много и размер ограничивающего параллелепипеда узла достаточно большой, узел разделяется. Полученные в результате разделения узлы проходят тот же
самый процесс. В процессе пустые узлы исключаются из последующей обработки, а узлы с полигонами отправляются назад функции SortNode
(это и есть рекурсия).
//Выделяем место для списка указателей на полигоны
Node->NumPolygons = Num;
Node->PolygonList = new unsigned long[Num];
//Сканируем список полигонов, сохраняя указатели и назначая их
Num = 0;
netlib.narod.ru |
407 |

Глава 8. Создание трехмерного графического движка
for(i = 0; i < m_NumPolygons; i++) {
//Добавляем полигон к списку, если он находится
//в заданной области пространства if(IsPolygonContained(&m_PolygonList[i],
XPos, YPos, ZPos, Size) == TRUE) Node->PolygonList[Num++] = i;
}
}
Последний фрагмент кода SortNode выделяет массив индексов, указывающих на массив m_Polygons. Поскольку массив m_Polygons
содержит данные всех полигонов, имеет смысл сократить объем используемой памяти, сохраняя в массиве PolygonList индекс элемента массива m_Polygons. (Индекс 0 в PolygonList указывает на данные полигона в m_Polygons[0], индекс 1 в PolygonList указывает на данные полигона в m_Polygons[1] и т.д.)
|
Когда приходит время визуализации, этот массив указателей |
|
используется для получения информации о каждом полигоне, включая |
|
индексы вершин и используемую группу материалов. Рисуемые полигоны |
|
позднее добавляются к буферу индексов соответствующей группы |
|
материалов с помощью функции AddNode. |
|
cNodeTreeMesh::IsPolygonContained и |
|
cNodeTreeMesh::CountPolygons |
|
Этот дуэт функций вы используете для того, чтобы определить, находится ли |
|
полигон в области трехмерного пространства, заданной ограничивающим |
|
параллелепипедом, и подсчитать общее количество полигонов, находящихся |
|
в заданной области. |
|
BOOL cNodeTreeMesh::IsPolygonContained(sPolygon *Polygon, |
|
float XPos, float YPos, float ZPos, |
|
float Size) |
|
{ |
|
float XMin, XMax, YMin, YMax, ZMin, ZMax; |
|
sVertex *Vertex[3]; |
|
// Получаем вершины полигона |
|
Vertex[0] = (sVertex*)&m_VertexPtr[m_VertexSize * |
|
Polygon->Vertex[0]]; |
|
Vertex[1] = (sVertex*)&m_VertexPtr[m_VertexSize * |
|
Polygon->Vertex[1]]; |
|
Vertex[2] = (sVertex*)&m_VertexPtr[m_VertexSize * |
|
Polygon->Vertex[2]]; |
|
// Проверяем ось X заданной области пространства |
|
XMin = min(Vertex[0]->x, min(Vertex[1]->x, Vertex[2]->x)); |
|
XMax = max(Vertex[0]->x, max(Vertex[1]->x, Vertex[2]->x)); |
|
if(XMax < (XPos - Size / 2.0f)) |
|
return FALSE; |
|
if(XMin > (XPos + Size / 2.0f)) |
|
return FALSE; |
|
В показанном выше коде (а также и в показанном ниже) определяется |
|
вершина, наиболее удаленная по оси X. Если полигон слишком далеко |
|
|
408 |
netlib.narod.ru |