В этом уроке мы, наконец-то, выведем на экран что-нибудь отличающееся от равномерного окна-прямоугольника, фигурировавшего в предыдущих уроках.
Чтобы вывести на экран 3D-объект, нам нужно загрузить в DDD данные его меша, а именно координаты вершин, вектора нормалей, текстурные координаты и массив индексов для построения граней. Один из конструкторов класса Меш позволяет всё это передать в качестве параметров. Но в этом уроке мы пойдём по другому пути. Обычно, 3D-модели (и даже целые сцены из нескольких 3D-объектов) рисуют в 3D-редакторе, потом производится экспорт этих моделей в файлы определённого формата, а 3D-библиотека, такая как DDD, позволяет загружать эти файлы и использовать информацию из них для вывода 3D-объектов на экран. Второй конструктор класса Меш принимает путь к такому файлу с данными меша (с расширением .me). Но напрямую мы и им не воспользуемся, вместо этого мы получим готовый объект класса Меш через систему ресурсов (конструктор будет вызван внутри неё).
Ресурсы – это удобный и предпочтительный способ загрузки внешних файлов в DDD. Поддерживаются ресурсы следующих видов:
меши – файлы с расширением .me
файлы c информацией о скелетной анимации мешей, или, попросту, скелеты – файлы с расширением .sk
графические файлы для использования в текстурах, поддерживаются форматы JPG, PNG, TIF, TGA
файлы описания инфопанелей в формате JSON, с расширением .ip
файлы шрифтов, поддержваются форматы .ttf, .fon
Вступительная болтовня оказалась неожиданно длинной, пора уже что-то сделать.
Для целей этой серии уроков были подготовлены несколько ресурсов различных типов, которые расположены здесь: http://striver00.ru/где-то. Загрузите этот архив и разархивируйте содержимое в каталог bin.
Нам надо передать в DDD информацию о расположении каталогов с нашими ресурсами. В разархивированном каталоге ресурсы находятся пять подкаталогов: инфопанели, меши, скелеты, текстуры и шрифты. В идеале нам надо передать в функцию инициализация_ddd массив с путями ко всем этим каталогам. Но мы пока ограничимся одним из них – каталогом меши, т. к. во-первых, в этом уроке всё остальное нам не понадобится, а во-вторых, и это даже важнее, в следующем уроке нам снова придётся переделать это место программы. Пока мы в файле urok.d в функции main поправим первую строку. Сейчас в функцию инициализация_ddd в качестве аргумента передаётся пустой массив. Заменим эту строку на такую:
инициализация_ddd(["ресурсы/меши"]);
В этом уроке мы отобразим меш с одним из 3D-объектов, встроенных в 3D-редактор Блендер в качестве стандартного примитива – головой обезьянки Сюзанны. Файл с этим мешем называется Сюзанна.me, ресурс будет называться просто Сюзанна. Откройте файл scena.d, добавьте в конец строки импорта ещё один модуль ddd.ddd_object (он позволяет использовать класс Объект), и перейдите к концу функции сцена:
Запросим меш у ddd-менеджера:
auto меш1 = менеджер.получить_меш("Сюзанна");
Создадим объект Места, в котором будет находиться наш 3D-объект:
auto место1 = менеджер.начальное_место.создать_ребёнка("Место объекта1",
Вектор3.НОЛЬ,
Кватернион(0.7459,0.6661,0,0));
Не пугайтесь заранее страшных чисел в параметрах кватерниона направления — это всего лишь поворот на 90 градусов. Как вариант, можно было вместо Кватернион(0.7459,0.6661,0,0) написать Кватернион(PI/2, Вектор3(1,0,0)), но тогда надо было бы импортировать модуль стандартной библиотеки std.math для получения константы PI. Если такой поворот вообще не делать, пришлось бы любоваться головой Сюзанны снизу.
Следующие две строки требуется обернуть в условный оператор if, в котором мы убедимся, что меш из файла действительно загрузился, и переменная меш1 не содержит null. Таким образом, в случае какой-либо ошибки в файле Сюзанна.me наша программа просто не выведет обезьянку на экран, а если этого не сделать, «упадёт» с ошибкой (в Linux она называется «ошибка сегментирования», в Windows – «Access violation»).
if (!(меш1 is null)) { }
Теперь (уже внутри фигурных скобок, соданных в предыдущем пункте) создадим 3D-объект с нашим мешем:
auto объект1 = new Объект("объект1", меш1);
И, наконец, присоединим его к ранее созданному объекту Место:
место1.присоединить_объект(объект1);
К объекту Место можно присоединять любое количество объектов, так что мы могли бы, например, не создавать новое Место в пункте 3.b, а присоединить объект нашей обезьянки к Месту одной из ламп, созданных в предыдущем уроке. Но тогда эта лампа не смогла бы осветить наш объект, т. к. находилась бы внутри него.
Компиляция и запуск нашего проекта должны отобразить нечто подобное:
Попробуйте загрузить вместо Сюзанны другие меши, присутствующие к каталоге ресурсы/меши.