реализация шаблона MVC на графе сцены C ++ - PullRequest
1 голос
/ 26 ноября 2010

У меня есть дерево объектов, которые представляют 3D-модель (граф сцены). Узлы дерева бывают разных типов (реализованы как производные от общего базового класса). Например, есть узел, представляющий многоугольник, или узел, который применяет преобразование координат (вращение, перемещение) к своим дочерним узлам. Также необходимо, чтобы сторонние поставщики могли внедрять новые типы узлов и добавлять их с помощью плагина (я использую Qt в качестве инфраструктуры GUI). Поэтому возможно, что в дереве могут быть узлы, тип которых неизвестен при компиляции.

Теперь я хочу реализовать класс, который действует как представление для этого графа сцены. Для каждого типа узла дерева представление должно выполнить соответствующие действия (нарисовать многоугольник, преобразовать и т. Д.). Моя идея состоит в том, чтобы реализовать классы представления для каждого типа узла и позволить классу представления верхнего уровня делегировать эти классы в зависимости от типа узла. (Сторонние поставщики смогут реализовать собственные классы делегатов представления)

Итак, мой вопрос: как я могу определить тип узла быстрым и расширяемым способом?

Мои идеи пока:

  1. Я мог бы добавить идентификатор типа для каждого класса узла. Это может быть просто целое число (строки не подходят по соображениям производительности). Проблема заключается в управлении идентификаторами типов для сторонних поставщиков. Как я могу убедиться, что один и тот же идентификатор не используется для разных типов узлов (например, разными поставщиками)?

  2. Я мог бы реализовать код рисования или, по крайней мере, вызов соответствующего объекта делегата рисования непосредственно в узле. Но моим объектам-узлам желательно не знать что-либо об их объектах вида. Также невозможно предоставить каждому объекту-узлу выделенный объект вида (речь идет о десятках тысяч узлов).

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

Спасибо заранее, McNumber

Ответы [ 3 ]

2 голосов
/ 26 ноября 2010

Граф сцены обычно живет в слое View системы MVC. В конце концов, сцена подразумевает ту часть, которую вы видите. Как правило, после установки правильного контекста OpenGL (или любого другого чертежа, который вы используете для определения в качестве эквивалента), можно было бы вызвать некоторый метод «рендеринга» в корневом узле графа сцены, а затем он рекурсивно отображает всех его потомков.

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

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


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

class SceneNode
{
  public:
    virtual void render() = 0;
};

Самое очевидное, что можно сделать из этого, - создать узел с дочерними элементами, чтобы у нас на самом деле было дерево узлов.

class ListSceneNode : public SceneNode
{
  private:
    typedef std::vector<std::shared_ptr<SceneNode> > ChildList;
    ChildList children;

  public:
    void render() { 
      for(ChildList::iterator i = children.begin() ; i != children.end(); ++i)
        i->render();
    }
};
0 голосов
/ 20 декабря 2012

Неактивная тема некоторое время, но мне интересно, если вы достигли прогресса с тех пор и / или все еще работаете в той же области.

У меня похожая проблема. Я разделяю существующую кодовую базу со всем, что слито воедино: Model (SceneGraph), GUI (win32) и рендеринг (OpenGL) в красивую платформу Model / View (без отдельного контроллера). Разделение Модель / Представление для GUI работает, и теперь я работаю над рендерингом OpenGL. В качестве первого «простого» требования для себя я начал ограничивать систему в том смысле, что на уровне модели не допускается включение заголовка OpenGL. Это немедленно вынуждает вас иметь отдельный OpenGL «RenderGraph» на стороне вида в дополнение к SceneGraph на стороне модели. Все возможные узлы в SceneGraph получают свое собственное представление вида (как вы указали). Мне это нравится, потому что типичные данные рендеринга OpenGL (например, массивы вершин) остаются на стороне представления. Например, можно представить рендеринг трассировки лучей, который бесполезен для этих данных, поэтому хорошо не включать его в модель (только такие вещи, как «радиус» или «положение»).

Иерархия SceneGraph и Rendergraph в настоящее время является симулированной, хотя я мог бы предположить, что в зависимости от изменений состояния может быть другая структура (как упоминает TokenMacGuy). Когда изменяется SceneGraph (запускаемый графическим интерфейсом и распространяемый моделью), вы должны обрабатывать это уведомление об изменениях локально в RenderGraph. Это включает в себя такие действия, как добавление / удаление / перемещение объектов. Это немного громоздко, но управляемо.

Моя основная проблема на данный момент заключается в том, что отношение между SceneGraph (Модель) и «ViewGraph» (View / GUI) немного отличается от отношения между SceneGraph и RenderGraph (View / OpenGL). В то время как в первом случае вы можете иметь «горизонтальные» соединения между узлами модели и узлами представления, во втором случае смена промежуточного узла часто вызывает повторную визуализацию всего RenderGraph, начиная с корневого узла и выше. Я не нашел элегантного способа включить это в свои рамки.

В качестве примера, скажем, у вас есть модель автомобиля в вашем SceneGraph. Где-то в листе у вас есть «диаметр клапана передней левой шины». Это будет представлено в текстовом элементе управления в ViewGraph, и когда пользователь изменяет его, соответствующий лист в SceneGraph корректируется. Нет проблем, это локальное изменение. Модель получает это изменение и отправляет уведомление RenderGraph для обновления рендеринга OpenGL. Узел в RenderGraph, получающий это уведомление, теперь должен не только заново визуализировать себя, но и весь RenderGraph в целом должен быть перерисован.

С уважением, Даниэль Деккерс

0 голосов
/ 26 ноября 2010

Как насчет разбиения операции просмотра на набор простых операций (перемещение в положение (x, y), рисование линии длиной N и т. Д.) Как части класса рендеринга. Тогда каждый узел модели может иметь свою собственную операцию рендеринга, в которой он вызывает операции над объектом рендеринга, который имеет эти простые операции в своем интерфейсе.

Реализация класса render может реализовать эти простые операции в терминах вызовов Qt. Затем класс View верхнего уровня просто вызывает метод render на узлах (он также может передать соответствующий объект render в качестве аргумента, если вы не хотите, чтобы он был постоянным атрибутом класса узла).

...