2010-11-26 57 views
1

我有一个代表3D模型(场景图)的对象树。树节点具有不同的类型(作为来自公共基类的派生类实现)。例如,有一个表示多边形的节点,或者一个将坐标变换(旋转,平移)应用到其子节点的节点。这也是一个要求,第三方供应商应该能够实现新的节点类型,并通过插件(我使用Qt作为GUI框架)添加它们。因此,编译时可能存在树中的节点,其类型未知。在C++场景图上实现MVC模式

现在我想实现一个类,作为这个场景图的视图。对于每个树节点类型,视图必须采取适当的操作(绘制多边形,变换等)。我的想法是为每个节点类型实现视图类,并根据节点类型让顶级视图类委派给这些类。 (第三方供应商将能够实现自己的查看代理类)

所以我的问题是:我如何确定一个节点的性能和可扩展的方式?

我的想法而已:

  1. 我可以一个类型标识符添加到每个节点类。这可能只是一个整数(字符串不适合性能方面的原因)。问题在于管理第三方供应商的类型标识符。我怎样才能确保相同的标识符不用于不同的节点类型(例如不同的供应商)?

  2. 我可以实现绘图代码,或者直接在节点中直接调用相应的绘图委托对象。但是我的节点对象最好不知道他们的视图对象。也不可能给每个节点对象一个专用的视图对象(我们正在谈论成千上万的节点)。

那么,你有什么想法?是否有完全不同的方式来处理这个问题?记住:解决方案不应该要求哈希表查找或其他计算密集型算法,因为我需要实时绘制图形。

由于提前, McNumber

回答

2

一个场景图通常寄居在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

谢谢你的答案!在某种游戏中,这是完全可以接受的。但是我必须开发一个3D-CAD应用程序。该树用于存储关于当前编辑模型的实体的所有信息。此外,同样的树被用于模拟,测试等。为这些任务分配不同的树将是毫无意义的,因为它们大多是相同的。我明确地想要分离模型(或许是比场景图更好的词)和绘图算法。 – McNumber 2010-11-26 09:03:15

0

什么打破观看操作为一组简单的操作(移动到(X,Y)的位置,绘制的N长度等的线),为使类的一部分。然后,每个模型节点都可以拥有自己的渲染操作,在该渲染操作中,它会在其接口中具有这些简单操作的渲染对象上调用操作。

渲染类的实现可以通过Qt调用实现这些简单的操作。顶层的View类然后只是在节点上调用渲染方法(它也可以传递适当的渲染对象作为参数,以防你不希望它成为节点类的永久属性)。

0

一段时间不活动的线程,但我想知道如果你从那时起取得进展和/或仍在同一领域工作。

我有类似的问题。我将一个现有的代码库与一切集中在一起:模型(SceneGraph),GUI(win32)和渲染(OpenGL)到一个很好的模型/视图框架(没有单独的控制器)。 GUI的Model/View分离工作,现在我正在处理OpenGL渲染。作为对我自己的第一个“简单”需求,我开始限制系统,因为在模型层中不允许包含OpenGL标头。这会立即强制您在视图端有一个单独的OpenGL“RenderGraph”,除了模型侧的SceneGraph。 SceneGraph中的所有可能的节点都可以获得他们自己的视图表示(就像你说的那样)。我喜欢这样,因为典型的OpenGL渲染数据(如顶点数组)保持在视图方面。例如,可以想象一个对此数据没有用处的光线跟踪渲染,因此最好不要将其包含在模型中(只包含“半径”或“位置”之类的东西)。

SceneGraph和Rendergraph的层次结构目前是相似的,但我可以想象,根据状态变化,可能会有不同的结构(如TokenMacGuy提到)。当SceneGraph改变时(由GUI触发,并由模型传播),你必须在RenderGraph本地处理这个改变通知。这涉及到添加/移除/移动对象等操作。这有点麻烦,但它是可管理的。

我现在的主要问题是SceneGraph(Model)和“ViewGraph”(View/GUI)之间的关系与SceneGraph和RenderGraph(View/OpenGL)之间的关系有点不同。在第一种情况下,您可以在模型节点和视图节点之间建立“水平”连接,而在第二种情况下,中间节点的更改通常会迫使整个RenderGraph从根节点向上重新渲染。我还没有找到一种将其纳入我的框架的优雅方法。

举一个例子,假设你在SceneGraph中有一辆汽车的模型。在叶子的某处,你有“左前轮胎的阀门直径”。这将在ViewGraph中的文本控件中表示,并且当用户更改它时,会调整SceneGraph中相应的叶子。没问题,这是一个本地变化。该模型接收到此更改并向RenderGraph发送通知以更新OpenGL渲染。 RenderGraph中接收此通知的节点现在不仅需要重新渲染自身,而且整个RenderGraph必须重新绘制。

亲切的问候, 丹尼尔戴克斯