Covellite++  Version: 2.3.0 Revision: 2580 Platform: x64 Build: 15:23 16.10.2020
Кроссплатформенный фреймворк для разработки приложений на С++
Компонентная система

Используемые термины

  • Компонент - объект, содержащий информацию об одном аспекте игрового объекта (таких как текстура, меш, положение и т.п.).

Игровой объект - объект в игровом мире, содержащий информацию в виде набора компонентов.

  • Игровой мир - набор игровых объектов, разбитый на иерархическую структуру из ячеек, каждая из которых содержит несколько игровых объектов; назначение - оптимизация подгрузки и выгрузки игровых объектов (сразу ячейками).
  • Игровая сцена - набор игровых объектов, непосредственно участвующих в игровом процессе; объекты подгружаются (по мере необходимости) из игрового мира и это именно те объекты, которые должны обновляться в процессе игры.
  • Рендер - объект, предназначенный для отображения игровых объектов в 3D сцене, создается на основе одного или нескольких компонентов.
  • Объект 3D сцены - объект (набор рендеров), из которых состоит 3D сцена.
  • 3D сцена - набор объектов 3D сцены, которые отображаются в окне программы; является 'зеркалом' той части объектов игровой сцены, которые попадают в поле зрения камеры.

Исходные требования

  • Не должно производиться никаких лишних действий (вызовов пустых функций), для каждого объекта (игрового и 3D сцены) должен выполнятся набор только ему необходимых действий.
  • Отсутствие дублирования информации (все объекты, отрисовываемые при помощи одного меша, должны содержать ссылку на единую область памяти с информацией об этом меше).

Компоненты

Фреймворк предоставляет класс компонента Component, а также механизм создания рендеров для этих компонентов с учетом активного графического Api (рендеры создаются через объект класса Renders, который может быть получен при помощи функции GetRenders() класса Window).

Класс компонента:

  • Содержит и предоставляет доступ к параметрам, которые используются для создания рендеров.
  • Параметры (кроме обязательных) могут отсутствовать, для таких параметров предусмотрено значение по умолчанию.
  • Обязательный параметр id - идентификатор компонента. Для компонентов с одним и тем же идентификатором будет использоваться один и тот же рендер, поэтому объекты, содержащие один и тот же меш, могут содержать компоненты, у которых задан только (один и тот же) идентификатор, а развернутое описание этого компонента можно сделать в другом месте и создать его рендер заранее.
  • Обязательный параметр type - тип компонента, который определяет способ отрисовки этого компонента.
  • Параметр kind - дочерний подтип компонента; у некоторых типов не используется.
  • Первоначальная информация может загружаться в виде строк, в дальнейшем (для ускорения рендеринга) при обновлении игровых объектов значения параметров следует устанавливать того же типа, какой используют рендеры (подробнее см. в описании параметорв компонентов).
Заметки
Предполагается, что исходная информация о компоненте будет хранится в узлах xml файла, из которых параметры будут загружаться как атрибуты, у которых имя атрибута будет названием параметра, а значение атрибута - значением параметра.

Типы компонентов

Data

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

Заметки
  • Вспомогательные комопненты следует передавать основному компоненту как параметр service в виде набора std::vector<std::shared_ptr<Component>> (какие данные нужны конкретным рендерам см. в описании соответствующих компонентов).
  • Идентификатор этого компонента не используется.
kind Параметры Тип параметра (значение по умолчанию) Описание параметра
Position (1) x, y, z float (0.0f) Положение (координаты) в пространстве
Rotation (1) x, y, z float (0.0f) Ориентация (углы поворота вокруг соответствующей оси в радианах) в пространстве
Scale (1) x, y, z float (1.0f) Масштабирующие коэффициенты по соответствующим осям
Rect (1) left, top, right, bottom int Границы прямоугольника
Texture content (4) covellite::api::Buffer_t<uint8_t> Бинарные данные текстуры в формате R8G8B8A8
width int Ширина изображения в пикселях
height int Высота изображения в пикселях
name String_t Имя текстуры в шейдере GLSL
index int Индекс текстуры tX в шейдере HLSL
destination String_t (albedo) Назначение текстуры (albedo, metalness, roughness, normal, occlusion, depth)
mipmapping bool (false) Генерировать mip'ы для текстуры; только для текстур, не используемых как внеэкранные поверхности
mapper std::function<bool(const void *)> Функция обратного вызова для чтения данных текстуры (каждый пиксель - это число uint32_t в формате ABGR)
capacity int (8) Количество бит на каждый канал цвета (8, 16, 32)
Shader content (4) covellite::api::Buffer_t<uint8_t> Содержимое текстового файла шейдера в бинарном виде
entry String_t Имя функции точки входа шейдера
instance String_t Описатель (вида 'f4f4i4') структуры инстанс-буфера
Buffer content (4) covellite::api::Buffer_t<Vertex> Данные вертексного буфера для отрисовки плоских и объемных объектов (2)
covellite::api::Buffer_t<int> Данные индексного буфера
count size_t Количество элементов в инстанс-буфере
mapper std::function<bool(void *)> Функция обратного вызова для заполнения инстанс-буфера
std::function<bool(void *, size_t &)>
size size_t Размер инстанс-буфера в байтах

Компонент Data.Texture может использоваться для следующих целей:

  • Цель рендеринга во внеэкранную поверхность (см. компонент BkSurface).
  • Источник данных для рендеринга текстурированных объектов (см. компонент Texture).

В рамках одного прохода (одна и та же камера или BkSurface) Data.Texture может использоваться только в одном качестве, т.е. в первом проходе ее можно передать компоненту BkSurface и она будет использоваться как цель рендеринга (и ее нельзя будет использовать в качестве текстуры объекта), а во втором проходе ее же можно передать компоненту Texture и использовать для рендеринга объектов.

Заметки
  • Параметр capacity текстур предназначен исключительно для увеличения точности значений при передаче их между разными проходами рендеринга (мировых координат для расчета теней, например), использовать его для текстур, загруженных из файла не имеет смысла. Текстуры глубины всегда создаются как одноканальные, у которых 24 бита на пиксель.

Camera

Компонент виртуальной камеры.

kind Параметры Тип параметра (значение по умолчанию) Описание параметра
Orthographic service Data.Position
Perspective service Data.Position, Data.Rotation
distance float (0.0f) Расстояние от позиции, заданной компонентом Data.Position до камеры
fov float (90.0f) Угол поля зрения по вертикали в градусах
znear float (0.01f) Ближняя плоскость отсечения
zfar float (200.0f) Дальняя плоскость отсечения
- scale float (1.0f) Масштабирующий коэффициент размеров внеэкранной поверхности относительно размеров окна программы
width int (ширина окна программы) Ширина внеэкранной поверхности в пикселях
height int (высота окна программы) Высота внеэкранной поверхности в пикселях
Заметки
  • Камера определяет способ отрисовки сцены и ее рендер должен быть первым в списке 3D сцены.
  • Компонент формирует матрицы вида/проекции и передает их шейдеру (константный буфер шейдера CameraData).
  • Сформированные матрицы вида/проекции после вызова рендера будут записаны исходному объекту компонента камеры как параметры view и projection в виде объекта класса glm::mat4 (матрицы записываются в том же виде, что и при передаче шейдеру, т.е. транспонированными).
  • Параметры scale, width и height влияют на то, какого размера будет создана внеэкранная поверхность (компонент BkSurface), указанная после компонента камеры:
    • Если не указаны параметры scale, width и height, будет создана внеэкранная поверхность с размерами, совпадающими с размерами окна программы; при изменении размеров окна программы размеры внеэкранной поверхности также автоматически изменятся.
    • Если задан параметр scale, будет создана внеэкранная поверхность с размерами, равными размерам окна программы, домноженными на указанное значение (при этом значения параметров width и height будут проигнорированы); при изменении размеров окна программы размеры внеэкранной поверхности также автоматически изменятся с учетом указанного коэффициента.
    • Если не указан параметр scale и указаны параметры width и height, будет создана внеэкранная поверхность указанных размеров; при изменении размеров окна программы размеры внеэкранной поверхности меняться не будут.

Ортографическа камера использует левостороннюю систему координат, в которой:

  • Начало координат находится в левом верхнем углу экрана (viewport'ом в Windows является вся клиентская часть окна, в Android - все за исключением заголовка окна).
  • Координаты вдоль оси X увеличиваются вправо.
  • Координаты вдоль оси Y увеличиваются вниз.
  • Камера смотрит на плоскость Oxy со стороны положительных значений оси Z.
  • Одному пикселю окна программы соответствует изменение координаты на 1.0f.
Необходимо сделать:
  • Добавить картинку с осями координат для Windows и Android.

Перспективная камера использует правостороннюю систему координат:

  • Координаты вдоль оси X увеличиваются с запада на восток.
  • Координаты вдоль оси Y увеличиваются с юга на север.
  • Координаты вдоль оси Z (высота над плоскостью Oxy) увеливаются снизу вверх.
  • Камера смотрит в точку, заданную компонентом Data.Position с расстояния distance (т.е. реализован вид 'от третьего лица', если нужен вид 'от первого лица', установить distance в ноль).
  • Data.Rotation - это углы поворота самой камеры относительно точки Data.Position (т.е. направление, обратное направлению взгляда).
  • Отсутствие компонента Data.Rotation (нулевые значения углов ориентации) соответствует направлению взгляда из точки, смещенной от Data.Position на distance вдоль оси X в положительном направлении.

BkSurface

Компонент внеэкранной поверхности, предназначен для рендеринга в текстуру(ы).

kind Параметры Тип параметра Описание параметра
- service Data.Texture Набор текстур внеэкранной поверхности
width int Ширина внеэкранной поверхности в пикселях (только чтение)
height int Высота внеэкранной поверхности в пикселях (только чтение)

Подробнее о использовании компонента см. Рендеринг во внеэкранную поверхность.

State

Компонент изменения состояния конвеера рендеринга.

kind Параметры Тип параметра Описание параметра
Clear color uint32_t Цвет заливки буфера кадра
Depth enabled bool (false) Включение/отключение использования буфера глубины (используется инвертированный буфер глубины)
clear bool (false) Включение/отключение очистки буфера глубины
overwrite bool (true) Включение/отключение перезаписи буфера глубины
Blend Включение использования прозрачности
Sampler Включение использования сглаживания текстур
Scissor service Data.Rect
enabled bool В случае false Data.Rect не нужен
AlphaTest discard float Отбрасывание значений цвета, у которых значение Alpha-канала меньше или равно указанному
Заметки
Каждая камера при рендеринге отключает:
  • Использование внеэкранной поверхности (устаналивает вывод нап экран).
  • Использование буфера глубины.
  • Блендинг.

Shader

Компонент шейдера.

kind Параметры Тип параметра
- service Data.Shader (3)
Заметки
  • Тип создаваемого шейдера (вертексный или пиксельный) выводится автоматически из входного типа данных указанной функции точки входа.
  • Всем шейдерам в качестве заголовочных файлов передаются:
    • Файл, содержащий описания структур для передачи данных шейдеру (Fx).
    • Файл, содержащий описания форматов входных данных шейдеров
  • Если не указаны бинарные данные шейдера (параметры data и count), вместо них будут использоваться шейдеры по умолчанию (те, что использует фреймворк для рендеринга Gui).
  • В том случае, если в тексте шейдера содержится ошибка, рендер не будет создан и попытка отрендерить объект без установленного шейдера может привести к падению видеодрайвера. Поэтому хорошей практикой является установка объекту камеры компонентов шейдеров по умолчанию (без передачи данных шейдера content), которые в этом случае позволят отрендерить объект 'абы как'.

Подробнее о том, как формируется окончательный текст шейдера, передаваемый компилятору, см. Шейдеры.

Texture

Компонент текстуры.

kind Параметры Тип параметра
- service Data.Texture (3)

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

  • Если заданы параметры name как TexEnvironment и index как 4, то текстура будет передана слоту, который для унификации шейдера под HLSL и GLSL следует объявлять и использовать как
    COVELLITE_DECLARE_TEX2D(TexEnvironment, 4); // объявление
    float3 Color = COVELLITE_TEX2D_COLOR(TexEnvironment, TexCoord).rgb; // использование
  • Если параметры name и index не указаны, слот текстуры в шейдере определяется по значению параметра destination
    COVELLITE_DECLARE_TEX2D(TexAlbedo, 0); // albedo
    COVELLITE_DECLARE_TEX2D(TexMetalness, 1); // metalness
    COVELLITE_DECLARE_TEX2D(TexRoughness, 2); // roughness
    COVELLITE_DECLARE_TEX2D(TexNormal, 3); // normal
    COVELLITE_DECLARE_TEX2D(TexOcclusion, 4); // occlusion
    COVELLITE_DECLARE_TEX2D(TexDepth, 5); // depth
  • Если не указаны параметры name, index и destination, слот текстуры в шейдере будет установлен как
    COVELLITE_DECLARE_TEX2D(TexDiffuse, 0);
Заметки
  • Параметр mapper используется для установки функции обратного вызова, которая вызывается при активации рендера и предназначена для чтения содержимого буфера текстуры (только для текстур, используемых в качестве внеэкранных поверхностей). Нужно понимать, что при этом данные текстуры копируются из видеопамяти в системную память, что является тяжелой операцией и использование ее при каждой активации текстуры (возврат true при каждом вызове функции обратного вызова со значением nullptr) очень сильно ударит по производительности. Чтение данных из текстуры имеет смысл использовать в качестве особой, разовой операции; например, при обработке клика мышью.

Buffer

Компонент буферов геометрии меша и констант шейдера.

kind Параметры Тип параметра Описание параметра
Vertex service Data.Buffer (3)
mapper std::function<bool(const Vertex *)> Функция обратного вызова для изменения буфера
Constant service Data.Buffer (3)
mapper std::function<bool(const void *)> Функция обратного вызова для заполнения пользовательского буфера
size size_t Размер константного буфера в байтах (когда в качестве mapper используется std::function<bool(void *)>)
name String_t Имя переменной пользовательского буфера в шейдере ('cbUserData' по умолчанию)
Необходимо сделать:
Требуется обобщение способа передачи параметров константного и инстанс-буфера, т.к. они совпадают, но в первом случае они передаются самому компоненту, а во втором - компоненту Data.Buffer.
Заметки
  • Параметр kind можно не указывать, тип буфера выводится автоматически из типа переданных ему данных.
  • Порядок обхода вершин вертексного буфера - против часовой стрелки.
  • Если передан параметр mapper, будет создан буфер, который можно изменять (только содержимое, причем можно изменять только часть значений; размер буфера, равный значению параметра count изменить нельзя) прямо во время работы программы. Переданный функциональный объект будет вызываться два раза:
    • Со значением параметра nullptr - вернуть true или false в зависимости от того, следует ли обновлять буфер или нет.
    • С указателем на начало памяти буфера, если обновление требуется (возращаемое значение не используется).

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

  • При загрузке сцены все объекты, являющиеся точечными источниками света, добавляют свои параметры в общий список источников света сцены.
  • Камере добавляется компонент Buffer.Constant, функция обратного вызова которого (mapper) формирует значения фонового и направленного источников света.
  • Каждому объекту сцены добавляется компонент Buffer.Constant пользовательского константного буфера, функция обратного вызова которого перебирает список всех точечных источников света и добавляет в список источников света объекта те, которые освещают его сильнее всего (например, самые близкие).
  • Расчет радиуса действия для точечного источника света можно посмотреть здесь.
  • Для статичных объектов набор статичных же источников света можно формировать один раз.

Transform

Компонент установки положения/ориентации/масштабирования объекта.

kind Параметры Тип параметра Описание параметра
- service Data.Position/Rotation/Scale (3) Объект, положение/ориентация/масштаб которого могут изменятся во время работы программы
Static Объект, положение/ориентация/масштаб которого не будут изменятся во время работы программы
Billboard Data.Position (3) Объект, всегда развернутый 'лицом' к камере
Заметки
  • Компонент формирует матрицу трансформации объекта и передает ее шейдеру (константный буфер шейдера ObjectData.World).
  • У статического объекта мировая матрица будет вычислена один раз при его создании и в дальнейшем изменить его положение/ориентацию/масштаб будет невозможно; динамический объект захватывает переданные ему компоненты Data, поэтому его положение/ориентацию/масштаб можно менять в каждом кадре изменяя значения параметров этих компонентов.
  • 'Лицом' billboard'a считается плоскость Oxy при взгляде со стороны положительных значений вдоль оси z.

Present

Компонент отправки информации в конвеер рендеринга.

kind Параметры Тип параметра
Index service Data.Buffer (3)
Instance Data.Buffer
Заметки
  • Компонент Present должен завершать набор компонентов каждого объекта 3D сцены.
  • Указание значения kind не обязательно; если задан только Buffer.Index, будет создан компонент Present.Index, если заданы и Buffer.Index и Buffer.Instance - Present.Instance.

Updater

Компонент обновления объекта. Позволяет установить функциональный объект (функцию обратного вызова), связанную с конкретным объектом.

Параметры Тип параметра Описание параметра
function std::function<void(const float)> Функциональный объект, который будет вызываться при активации рендера компонента
Заметки
  • Переданному функциональному объекту при вызове будет передаваться время рендеринга текущего кадра (отсчет ведется с момента запуска программы) в секундах.
  • Гарантируется, что в рамках рендеринга одного кадра все updater'ы всех объектов получат одно и то же значение времени.
  • Компоненту можно установить новый функциональный объект в любой момент времени (это может делать даже сам вызванный функциональный объект во время работы, безо всяких ограничений).

Если необходимо, чтобы updater объекта вызывался только тогда, когда рендерится сам объект (например, когда он находится в поле зрения камеры), то компонент updater'a можно добавить в список компонентов самого объекта и забыть о нем. Если же updater нужно вызывать независимо от того, рендерится объект в текущем кадре или нет, то необходимо компонент его updater'a поместить в отдельный игровой объект и вызывать его рендер индивидуально.

Сноски

  1. Параметры этих компонентов могут изменяться во время работы программы (т.е. для изменения положения объекта в пространстве не нужно его пересоздавать, достаточно установить новые координаты комопоненту его положения), для изменения остальных (например замены 3D модели объекта) необходимо создать новый набор компонентов, удалить из 3D сцены старый и создать новый объект.
  2. Параметры можно указывать непосредственно основному компоненту, без использования компонента Data.
  3. Может быть несколько компонентов, что можно использовать для привязки (совместного движения) объектов друг к другу (в этом случае необходимо гарантировать, чтобы в качестве общего компонента использовался ОДИН И ТОТ ЖЕ объект, совпадения идентификаторов компонентов недостаточно!).
  4. Содержимое параметра content будет удалено из компонента во время создания рендера.

Общая концепция использования компонентов

Основная идея работы с 3D объектами предполагает использование архитектуры программы на основе паттерна MVC.

Модель

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

Представление

  • Класс окна, которое создается между окнами Api и Gui.
  • Содержит, создает и удаляет объекты 3D сцены на основе информации, полученной от модели.
  • При построении/обновлении 3D сцены первой должна идти камера.
  • Изменение геометрии/материала производится удалением/добавлением игрового объекта.

Взаимодействие частей кода игры

  • Панель GUI с кнопками действий игрока (View) подписывается на события нажатия кнопок, при получении которых генерирует события действий игрока.
  • Игровой мир (Model) подписывается на события действий игрока:
    • Обновляет состояние игрока и объектов игровой сцены.
    • В очередь создания объектов добавляются объекты, которые теперь попадают в зону действия камеры.
    • Из игровой сцены удаляются объекты, расположенные слишком далеко от камеры.
  • 3D сцена (View):
    • По событию создания объекта запрашивает у модели набор компонентов объекта и создает его рендер.
    • По событию удаления объекта удаляет его рендер.
    • По событию отрисовки окна запрашивает у модели актуальынй набор игровых объектов и активирует их рендеры.

Примеры использования компонентов

Отрисовка 2D объектов

Отрисовка 3D объектов