380
Грязный флаг (Dirty Flag) —
Паттерны программирования игр
Затем набросаем класс объекта в графе сцены. Это
необходимый минимум для реализации данного паттерна:
class GraphNode
{
public:
GraphNode(Mesh* mesh)
: mesh_(mesh),
local_(Transform::origin()) {}
private:
Transform local_;
Mesh* mesh_;
GraphNode* children_[MAX_CHILDREN];
int numChildren_;
};
У каждого узла есть локальные преобразования,
описывающие его положение относительно родителя.
А также меш, который является самой графикой объ-
екта. (Мы позволим
mesh_
принимать значение
NULL
,
чтобы создавать невидимые объекты для группировки
других объектов.) Наконец, у каждого узла имеется на-
бор дочерних объектов, он может быть пустым.
Таким образом «граф сцены» — корень
GraphNode
,
чьи дети (внуки и так далее) — все объекты игрового
мира:
GraphNode* graph_ = new GraphNode(NULL);
// 3 | '…
Чтобы отрисовать граф сцены, нам надо пройти
по всему дереву узлов, начиная от корневого элемента,
и вызвать соответствующую функцию для меша каждо-
го узла с нужными глобальными преобразованиями:
void renderMesh(Mesh* mesh, Transform transform);
Мы не будем реализовывать его тут, но если бы все-
таки стали, этот метод выполнял бы все необходимое
для отрисовки меша объектов в нужном месте. И если
Паттерны программирования игр
— Паттерны оптимизации
381
мы сможем корректно его вызвать для всех узлов графа
сцены, мы будем счастливы.
Неоптимизированный проход
Давайте замараем руки и напишем простейший проход
по графу сцены, в котором позиция будет вычисляться
на ходу. Он будет не оптимальным, зато простым. Доба-
вим новый метод в
GraphNode
:
void GraphNode::render(Transform parentWorld)
{
Transform world = local_.combine(parentWorld);
if (mesh_) renderMesh(mesh_, world);
for (int i = 0; i < numChildren_; i++)
{
children_[i]->render(world);
}
}
Будем передавать в него преобразования родитель-
ского узла, используя
parentWorld
. Таким образом,
для корректного расчета преобразований
этого
узла
нам останется скомбинировать переданные преобра-
зования с его собственными локальными. Нам не нуж-
но проходить
вверх
по цепочке родителей, чтобы вычис-
лить преобразования, так как мы их вычисляем, пока
идем
вниз
.
Мы вычисляем глобальные преобразования и хра-
ним их в переменной
world
, затем отрисовываем меш,
если он есть. Наконец, мы переходим к дочернему объ-
екту, передавая ему глобальные преобразования
теку-
щего
узла. В конце концов, это простой компактный ре-
курсивный метод.
Чтобы отрисовать весь граф сцены, мы запускаем
процесс в корневом элементе:
graph_->render(Transform::origin());
Do'stlaringiz bilan baham: |