Войти
K5EngineСтатьи

Графическая система движка (K5Engine 1)

Автор:

Введение.

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

Базовые принципы в K5Engine

Есть несколько общих правил, которым подчиняется структура движка:

Именование классов, все имена классов начинаются с префикса T, так же есть стандартные указатели на классы, они начинаются с префикса p. Например класс TDevice, экземпляр класса Device и указатель на  устройство pTDevice.

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

Так же почти все основные объекты имеют специальные списки и менеджеры списков. Например это TSpriteList,  TSpriteListManager, TTextureList, TActionList и другие.

Если проверка параметров проходит не удачно, то система кидает исключение. Выбрасывается класс-исключение TBaseException, который несёт в себе сообщение о месте происшествия и описание ошибки. Более подробно о системе генерации исключений можно прочитать в отдельной статье:
класс-генератор исключений

В движке широко используются перечисления, они все начинаются с префикса en, например: enEventTypes, enColorComponent, enChangeValueDirection. Имена значений перечислений  начинаются с составного префикса EN_ + сокращение от имени самого перечисления. Например: EN_CVD_Set, EN_ACS_Run, EN_CC_Alpha. Единственное исключение — имена типов событий, они имеют только начальный префикс EN_, например: EN_SYSTEM, EN_ACTIVATE, EN_MOUSEMOTION.

У классов движка стандартизированы методы. Метод задания параметра всегда начинается с префикса Set, получение параметра — Get, увеличение или уменьшение параметра — Inc и  Dec. Например — у класса TPoint3D функции:

void Set(const float &x,const float &y);
void Set(const TPoint3D &Point);
float GetX() const;
void IncY(const float &Value);
void Dec(const float &x,const float &y);

Так же часто для удобства имеются переопределённые операторы, в частности оператор круглых скобок, которые можно использовать вместо функций Set.
Для получения прямого доступа к элементам классов, если такая возможность предоставлена, используются функции вида Get..Ptr, например GetPointPtr, GetActionsPtr.

Device в K5Engine

Класс-устройство играет большую роль в структуре движка: он создаёт окно приложение, классы, зависящие от платформы и рендера а также преобразовывает системные события в события движка.
Его базовая реализация — TBaseDevice, предоставляет интерфейс для создания классов-устройств, зависящих от типа системы (Windows, Linux, Mac-OS и тд). В данный момент есть две предметные реализации устройства — WinApiDX8Device и WinApiGLDevice которые, соответственно, предоставляют рендер через DirectX8 и OpenGL с обработкой событий через WinApi. Так же есть «высокоуровевая» реализация TDevice, благодаря которой не нужно беспокоиться о типе устройства.

Система координат

Сцена в движке по сути трёхмерная. Координаты x и y — позиция объекта на экране, z — его позиция по глубине. Центр системы координат находится в верхнем левом углу создаваемого окна. Так же и при обращении к текстурным координатам, их центр координат находится в верхнем левом углу текстуры. Координаты позиции графических объектов задаются float значением, то есть могут быть дробными. Глубина расположения объекта (координата z) измеряется от 0 до 100, чем больше значение координаты, тем глубже находится объект. 

Ресурсы графической системы: текстуры и шрифты

В данный момент в движке представлены такие ресурсы: текстуры и глифы шрифтов.
Файл текстуры представлен виртуальным классом TTexture, от которого уже наследуются TGLTexture и TDX8Texture. В то же время TGlyph не зависит от реализации, в нём есть указатель на  TTexture, по которому уже и создаётся текстура глифа.

Текстурами можно оперировать и через указатель на базовый класс, но для более удобного хранения и их использования были созданы два класса — список текстур и менеджер списков текстур: TTextureList и TTextureListManager. Они позволяют более удобно управлять текстурами, так же при удалении списка или менеджера автоматически будут удалены все подчинённые текстуры. В небольших проектах достаточно и списка текстур, для более крупных проектов удобно использовать менеджер. Например: использовать несколько списков текстур, в одном лежат текстуры интерфейса, в другом текстуры сцены, в третьем частицы, тогда что бы отчистить всё, что связано с сценой, достаточно просто отчистить список сцены или удалить его за ненадобностью.

Работа же с шрифтами отличается от работы с текстурами. TGlyph — это представление одного символа с текстурой, сформированной для использования в системе, TFont — набор глифов, который используется текстовой, TFontFaceList — исходный шрифт, из которого уже формируются глифы, которые может использовать система.

Графические объекты в K5Engine

В данный момент представлено три вида графических объектов: спрайт, банк спрайтов, текст.
Так же к каждому объекту добавлен список и менеджер списков.
Каждый графический объект наследуется от TBaseGraphicObject и имеет такие методы:

unsigned long GetID(); - получение индивидуального идентификатора объекта

void    SetName(const wstring &Val); - установка имени объекта
wstring GetName() const; - получение имени объекта

void SetView(const bool &Val = true); - установка флага отрисовки объекта
bool GetView() const; - получение флага отрисовки объекта

void SetDevice(const pTBaseDevice &Val); - установка устройства, пока устройство не будет установлено, объект не будет отрисовываться, так как устройство создаёт его класс-отрисовщик

pTBaseDevice GetDevice(); - получение устрйства.

void SetMinFilter(const enTextureFilter &Val); - установка минимального текстурого фильтра
void SetMagFilter(const enTextureFilter &Val); - установка максимального текстурого фильтра
void SetFilter(const enTextureFilter &Val); - установка обоих типов фильтров

enTextureFilter GetMinFilter() const; - получение минимального текстурого фильтра
enTextureFilter GetMagFilter() const; - получение максимального текстурого фильтра

void Draw(); - рисование графического объекта

От базового графического объекта наследуются три потомка: TSprite, TSpriteBank, TBaseText.

Спрайт

class TSprite: public TBaseGraphicObject
{
  protected:
    pTSpriteViewer   Viewer; 
    pTBaseVertexData VertexPool;
  protected:
    void ToSetDevice();
    void ToDraw();
  public:
    TPoint3D        Pos;
    TPoint3D        Center;
    TGraphicElementValue    Angle;
    TGraphicElementSquare     Square;

    TGraphicElementColorArray  Color;
    TGraphicElementSpriteTexture   Texture;
  public:
    TSprite();
    virtual ~TSprite();

    void operator=(const TSprite &Val);

    pTSpriteViewer GetViewer();
    void SetVertexPool(const pTBaseVertexData &Val);
    pTBaseVertexData GetVertexPool();
};
Viewer – класс-отрисовщик спрайта, как раз он отвечает за отображение спрайта в той или иной системе рендера.
VertexPool — пул вертиксов, отдельная система, позволяющая задавать внешний пул вертексов для группы спрайтов. Нужна, если надо обрабатывать большую группу объектов в сцене. Благодаря ему можно нексколько увеличить скорость рендера.
Методы void ToSetDevice() и void ToDraw() - это переопределённые функции базового класса.
Далее идут публичные члены класса. Структура графического объекта организована так, что работа с его параметрами идёт через специальные компоненты. Благодаря этому можно относительно безболезненно заменять или изменять тот или иной элемент а так же добавлять новые. Так же так как такие элемены стандартизированны, то к ним удобно обращаться на прямую, а не через множество отдельных функций.
Так же у спрайта переопределен оператор присваивания для возможности без проблем выполнять присваивание между объектами.

Теперь про элементы спрайта:
Pos – позиция на сцене. Задаётся тремя координатами x,y и z.
Center – смещение центра спрайта, так же задаётся тремя координатами.
Оба эти элемента — экземпляры класса TPoint3D, имеют методы увеличения, уменьшения, установки значения координаты а так же переопределённый опратор круглых скобок и возможность прямого доступа к внутренним переменным. Списко методов не приводится, так как он занимает много текста.
Далее идёт угол наклона спрайта, элемент Angle. Он является экземпляром класса  TGraphicElementValue, который имеет такие методы:

void operator()(const float &Val); - установка значения
void operator=(const float &Val); - установка значения

void  Set(const float &Val); - установка значения
void  Inc(const float &Val = 1.0f); - увеличение значения
void  Dec(const float &Val = 1.0f); - уменьшение значения

float Get() const; - получение значения

Небольшое пояснение: каждый TGraphicElement.. является наследником от TBaseGraphicElement, который представляет собой интерфейс для работы с виевером графического объекта. При изменении параметров, зависящих от типа рендера,  TBaseGraphicElement посылает сообщение виеверу, что произошли изменения и нужно обновить данные.

Далее идёт Square спрайта. Фактически можно было обойтись и без него, использовать два  TGraphicElementValue как Width и Height параметры, с другой стороны благодаря расширенному функционалу TGraphicElementSquare имеет больше возможностей.
Он имеет методы для установки высоты и ширины спрайта, разнообразных итераций размеров, получения параметров а так же даёт доступ к редактированию параметров каждой из четырёх точек сторон спрайта.

Color — цвет спрайта. Представляет собой экземпляр TGraphicElementColorArray созданный с одним элементом. Этот элемент сделан расширяемым и его можно использовать например для полигонов для придания цвета каждой вершине. Элемент имеет разнообразные методы задания параметров цвета и итерации цвета. В движке используется класс TColor с четырьмя компонентами — red green blue alpha. Компоненты цвета задаются в диапазоне от 0 до 1.

Texture — элемент, задающий текстуру спрайта. Спрайт не будет отображаться, если текстура не задана. Текстура является одним из главных элементов спрайта.
Есть два способа использования текстуры: использовать часть текстуры (задать Rect),  использовать всю текстуру с одним или несколькими повторениями по одной из осей. Так же можно зеркально отображать текстуру.
Её поведение задаётся при вызове тех или иных функций. Например для задания области используемой текстуры есть группа методов SetRect. Для задания повтора текстуры использется группа функций SetRepeat, так же можно получить доступ на прямую к текстурным координатам через функции GetPointPtr.

Банк спрайтов

Банк спрайтов — специальный графический объект, позволяющий хранить и управлять группой спрайтов. Он полезен в случаях, когда надо обрабатывать группу спрайтов как один объект, например: боевая машина, состоящая их нескольких блоков, эффект взрыва, который выводится при помощи частиц, кнопка интерфейса, состоящая из нескольких частей.
У него есть три публичных элемента, аналогичные спрайту:

TPoint3D      Pos; - позиция на сцене группы страйтов
TPoint3D      Center;  - смещение центра группы спрайтов
TGraphicElementValue  Angle; - угол наклона  группы спрайтов

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

Текст

Класс TText является наследником TBaseText который уже в свою очередь наследуется от TBaseGraphicObject. Такая структура осталась ещё от старых версий движка и пока что не была переделана к принятому построению графических обектов. В будущем планируется приведение текстовых объектов к единому виду, но его текущая структура уже отработана и используется на протяжении большого промежутка времени.
По сути текст в движке — это набор спрайтов с натянутыми на них текстурами букв и выставленных по определённым правилам. В TText — каждая строка это отдельный банк спрайтов, строки собраны в список банков спрайтов. Большая часть методов управления параметрами текста в  TText досталась от  TBaseText. Сам TText имеет такие методы:

float GetWidth()  const; - получение ширины области, ограничивающей текст
float GetHeight() const; - получение высоты области, ограничивающей текст
void SetLineSpacing(const float &Val);  - установка отступа между строками, по умолчанию равен 1,2 от высоты шрифта.
float GetLineSpacing() const; - получение отступа между строками
void SetAlignment(const enTextAlignment &Value); - установка выравнивания, принимает такие значени: EN_TA_Left, EN_TA_Center, EN_TA_Right. По умолчанию — EN_TA_Center.
enTextAlignment GetAlignment(); - получение выравнивания.
pTSpriteBankList GetLinesPtr(); - получение указателя на список банков спрайтов, в котором находятся строки и символы.
void SetVertexPool(const pTBaseVertexData &Val); - установка пула вершин
pTBaseVertexData GetVertexPool(); - получение пула вершин

От  TBaseText к  TText достались функции установки текста, шрифта, цвета текста, очистка текста. Так же можно без преобразований задавать текст как целое число или число с плаваюзщей точкой.

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

Списки графических объектов и менеджеры списков

Списки нужны для более удобного управления графическими объектами, они автоматически устанавливают устройство объектам, очередь рендера и пул вертексов, так же позволяют скрывать сразу группу объектов, удалять, сортировать в порядке расположения на сцене. Так же список объектов выполняет роль менеджера ресурсов. Списки представляют из себя шаблонный класс, который уже доопределяется для каждого типа графических  объектов.
В данный момент представлены такие списки: TSpriteList, TSpriteBankList, TTextList.
У каждого списка есть имя, которое используется, если его надо идентифицировать в менеджере. При удалении списка он автоматически удалит все объекты, находящиеся в нём.
Менеджеры списков предназначены для управления самими спискампи объектов. По своей структуре они аналогичны спискам, но с поправкой на нужный функционал.
Одна из особенностей — набор методов, позволяющий добавлять как список, так и графический объект в указанный список.

Очередь рендера

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

void Add(const pTBaseRenderQueueCell &Cell); - добавления ячейки рендера

void Del(const unsigned long &Id); - удаление ячейки рендера по идентификатору объекта
void Del(const pTBaseGraphicObject &Object); - удаление ячейки рендера по указателю на объект

void SetCellDraw(const unsigned long &Id,const bool &DrawFlag); - изменения флага отрисовки ячейки
bool GetCellView(const unsigned long &Id); - получение флага отрисовки объекта

void Draw(); - отрисовка очереди
void Clear(); - отчистка очереди
void Sort(); - сортировка очереди

Так же помещать и удалять объкты из очереди рендера можно вручную. Для этого нужно использовать классы-ячейки графических объектов: TSpriteRenderQueueCell, TSpriteBankRenderQueueCell и TTextRenderQueueCell. Все они наследники от виртуального класса TBaseRenderQueueCell.
Однако ручное добавление или удалениче ячеек если и может понадобиться, то крайне редко.

Пул вершин

TVertexDataPool — самая новая система движка, предназначеная для некоторой оптимизиции системы рендера за счёт сведения зависящих от платформы данных графических объектов в один массив вершин. При использовании большого числа спрайтов в одной сцене она позволяет достаточно ощутимо повысить количество кадров. Списки и менеджеры списков работают с пулом вершин автоматически, достаточно передать им указатель на него.
Сама работа с TVertexDataPool происходит по алгоритму: передал устройство, создал пул на определённое количество спрайтов, можно передавать указатель на него целевым системам и объектам. В данный момент пул имеет ограниченый размер и не может расширяться динамически. В будущем планируется добавить возможность его автоматического расширения при достижении лимита по размеру.


На данный момент всё.
Примеры к статье представлены группой уроков с второго по седьмой:
Исходный код: Tutorials
Бинарные сборки: Tutorials

20 июля 2010 (Обновление: 13 авг. 2010)

Комментарии [33]