Этот совет не является специфическим для рендеринга, но должен помочь создать систему, которая в значительной степени разделяет вещи. Сначала попытайтесь сохранить данные GameObject отдельно от информации о местоположении.
Стоит отметить, что простая позиционная информация XYZ может быть не такой простой. Если вы используете физический движок, то данные о вашем местоположении могут храниться в стороннем движке. Вам нужно будет либо синхронизировать их (что потребует большого количества бессмысленного копирования памяти), либо запросить информацию непосредственно из движка. Но не всем объектам нужна физика, некоторые из них будут зафиксированы на месте, так что простой набор поплавков там отлично работает. Некоторые из них могут даже быть прикреплены к другим объектам, поэтому их позиция фактически является смещением другой позиции. В расширенной настройке положение может храниться только на графическом процессоре, единственное время, которое потребуется на стороне компьютера, - это скрипты, хранилище и сетевая репликация. Таким образом, у вас, вероятно, будет несколько возможных вариантов для ваших позиционных данных. Здесь имеет смысл использовать наследование.
Вместо того, чтобы объект, владеющий его положением, этот объект сам должен принадлежать структуре данных индексации. Например, у «Уровня» может быть Octree или, возможно, «сцена» физического движка. Когда вы хотите выполнить рендеринг (или настроить сцену рендеринга), вы запрашиваете в вашей специальной структуре объекты, которые видны камере.
Это также помогает обеспечить хорошее управление памятью. Таким образом, объект, который на самом деле не находится в области, даже не имеет позиции, которая имеет смысл, вместо того, чтобы возвращать 0,0 координат или координат, которые были у него, когда он был последним в области.
Если вы больше не сохраняете координаты в объекте, вместо object.getX () у вас получится level.getX (object). Проблема с поиском объекта на уровне, вероятно, будет медленной операцией, так как ему придется просматривать все свои объекты и соответствовать тому, который вы запрашиваете.
Чтобы избежать этого, я бы, вероятно, создал специальный класс 'link'. Тот, который связывает между уровнем и объектом. Я называю это «Место». Это будет содержать координаты XYZ, а также дескриптор уровня и дескриптор объекта. Этот класс ссылок будет храниться в пространственной структуре / уровне, и объект будет иметь слабую ссылку на него (если уровень / местоположение будет уничтожен, необходимо изменить ссылку на объект на null. Возможно, также стоит иметь класс Location на самом деле «владеть» объектом, таким образом, если уровень удаляется, то же самое относится и к специальной структуре индекса, расположению, которое он содержит, и его объектам.
typedef std::tuple<Level, Object, PositionXYZ> Location;
Теперь информация о местоположении хранится только в одном месте. Не дублируется между объектом, пространственной структурой индексации, рендерером и т. Д.
Пространственные структуры данных, такие как Octrees, часто даже не нуждаются в координатах объектов, которые они хранят. Там положение сохраняется в относительном расположении узлов в самой структуре (это можно рассматривать как своего рода сжатие с потерями, жертвуя точностью ради быстрого времени поиска). С объектом местоположения в Octree тогда фактические координаты находятся внутри него, как только запрос сделан.
Или, если вы используете физический движок для управления местоположением ваших объектов или их сочетанием, класс Location должен обрабатывать это прозрачно, сохраняя весь ваш код в одном месте.
Еще одним преимуществом теперь является положение и ссылка на уровень хранится в том же месте. Вы можете реализовать object.TeleportTo (other_object) и заставить его работать на разных уровнях. Точно так же поиск пути ИИ может следовать за чем-то в другую область.
Что касается рендеринга. Ваш рендер может иметь аналогичную привязку к местоположению. За исключением того, что там будут конкретные вещи для рендеринга. Вы, вероятно, не нуждаетесь в 'Object' или 'Level', чтобы быть сохраненными в этой структуре. Объект может быть полезен, если вы пытаетесь сделать что-то вроде выбора цвета или рендеринга хитбара, плавающего над ним и т. Д., Но в противном случае средство визуализации заботится только о сетке и тому подобном. RenderableStuff будет Mesh, также может иметь ограничивающие рамки и так далее.
typedef std::pair<RenderableStuff, PositionXYZ> RenderThing;
renderer.render(level, camera);
renderer: object = level.getVisibleObjects(camera);
level: physics.getObjectsInArea(physics.getCameraFrustrum(camera));
for(object in objects) {
//This could be depth sorted, meshes could be broken up and sorted by material for batch rendering or whatever
rendering_que.addObjectToRender(object);
}
Возможно, вам не нужно делать это каждый кадр, вы можете убедиться, что вы берете больший регион, чем показывает текущая камера. Кэшируйте его, отслеживайте движения объекта, чтобы увидеть, находится ли ограничивающий прямоугольник в пределах диапазона, отслеживайте движение камеры и так далее. Но не начинайте возиться с такими вещами, пока не сравните их.
Ваш физический движок может иметь аналогичную абстракцию, так как ему также не нужны данные объекта, только сетка столкновений и физические свойства.
Все ваши основные данные объекта будут содержать имя меша, который использует объект. Затем игровой движок может загружать его в любом формате, который ему нравится, не обременяя ваш класс объектов кучей специфичных для рендеринга вещей (которые могут быть специфичными для вашего API рендеринга, то есть DirectX против OpenGL).
Это также держит различные компоненты отдельно. Это позволяет легко выполнять такие вещи, как замена вашего физического движка, поскольку эти вещи в основном находятся в одном месте. Это также делает юнит-тестирование намного проще. Вы можете тестировать такие вещи, как физические запросы, без необходимости устанавливать какие-либо поддельные объекты, поскольку все, что вам нужно, это класс Location. Вы также можете оптимизировать вещи проще. Это делает более очевидным, какие запросы нужно выполнять для каких классов и отдельных мест для их оптимизации (например, вышеупомянутый level.getVisibleObject был бы местом, где вы могли бы кэшировать вещи, если камера не перемещается слишком сильно).
m_renderable
членом. Таким образом, вы можете лучше отделить свою логику. Не применяйте визуализируемый «интерфейс» к общим объектам, которые также имеют физику, ai и еще много чего. После этого вы можете управлять визуализируемыми объектами отдельно. Вам нужен уровень абстракции по сравнению с вызовами функций OpenGL, чтобы еще больше отделить вещи. Поэтому не ожидайте, что хороший движок будет иметь какие-либо вызовы GL API внутри своих различных визуализируемых реализаций. Вот и все, в двух словах.