2010-11-26 16 views
1

J'ai un arbre d'objets qui représente un modèle 3D (un graphe de scène). Les nœuds d'arbre sont de types différents (implémentés comme des classes dérivées d'une classe de base commune). Par exemple, il existe un nœud représentant un polygone ou un nœud qui applique une transformation de coordonnées (rotation, translation) à ses nœuds enfants. C'est aussi une exigence, que les fournisseurs tiers devraient être en mesure d'implémenter de nouveaux types de nœuds, et les ajouter par un plugin (j'utilise Qt comme framework GUI). Il est donc possible qu'il y ait des nœuds dans l'arbre, dont le type est inconnu lors de la compilation.Mise en œuvre d'un modèle MVC sur un graphe de scène C++

Maintenant je veux implémenter une classe, qui agit comme une vue pour ce graphe de scène. Pour chaque type de nœud d'arbre, la vue doit prendre les mesures appropriées (dessiner le polygone, transformer, etc.). Mon idée est d'implémenter des classes d'affichage pour chaque type de noeud et de laisser la classe de vue de niveau supérieur déléguer à ces classes en fonction du type de noeud. (Les fournisseurs tiers seront en mesure d'implémenter leurs propres classes de délégués de vue)

Donc, ma question est: comment puis-je déterminer le type d'un nœud d'une manière performante et extensible?

Mes idées à ce jour:

  1. je pourrais ajouter un identifiant de type à chaque classe de noeud. Cela pourrait simplement être un entier (les chaînes ne conviennent pas pour des raisons de performances). Le problème est la gestion des identifiants de type pour les fournisseurs tiers. Comment puis-je m'assurer que le même identificateur n'est pas utilisé pour différents types de noeuds (par exemple par différents fournisseurs)?

  2. Je pourrais implémenter le code de dessin, ou au moins un appel à l'objet délégué de dessin approprié directement dans le nœud. Mais les objets de mes noeuds ne devraient de préférence pas connaître leurs objets de vue. De même, il n'est pas possible de donner à chaque objet nœud un objet de vue dédié (on parle de dizaines de milliers de nœuds).

Alors, quelles sont vos idées? Y a-t-il peut-être une façon complètement différente de gérer cela? Rappelez-vous: la solution ne devrait PAS nécessiter de recherche de tables de hachage ou d'autres algorithmes à calcul intensif, car j'ai besoin de dessiner le graphique en temps réel.

Merci à l'avance, McNumber

Répondre

2

Un graphique de scène vit normalement dans la couche Affichage d'un système MVC. Après tout, scène sorte de implique la partie que vous voyez. En règle générale, après avoir défini le contexte OpenGL approprié (ou quel que soit le dessin que votre utilisateur définit comme équivalent), vous appelez une méthode "render" sur le nœud racine du graphe de scène, puis il restitue récursivement tous ses descendants.

Les graphiques de scène ne représentent pas souvent d'autres types d'état. Par exemple, dans un jeu avec simulation de physique, vous gardez un graphique de scène autour pour effectuer le rendu, mais une liste d'objets de physique est maintenue séparément par le moteur physique, et il suit une structure très distincte. Les moteurs physiques fonctionnent mieux si les objets physiquement proches les uns des autres sont également traversés de manière locale. Le rendu fonctionne mieux si les objets ayant des caractéristiques de rendu similaires (faites à partir des mêmes textures) sont traversés de manière locale.

Ainsi, un nœud sur le graphe de scène devrait savoir comment rechercher la position de l'instance de modèle qu'il représente, l'émettre vers le moteur de rendu et ensuite émettre les primitives de dessin pour ce type d'objet.


avec celle de la route, la mise en œuvre en fait une telle chose signifie probablement penser aux types d'interactions, au niveau mondial, le nœud racine du graphe de scène doit répondre. Dans le cas typique, cela signifie probablement le rendu. La chose la plus évidente à faire à partir de là est de faire un nœud qui a des enfants, de sorte que nous ayons en fait un arbre de nœuds.

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

Merci pour la réponse! Dans une sorte de jeu, ce serait parfaitement acceptable. Mais je dois développer une application 3D-CAD. L'arbre est utilisé pour stocker toutes les informations concernant les entités du modèle actuellement édité. En outre, le même arbre est utilisé pour des simulations, des tests et ainsi de suite. Avoir des arbres différents pour ces tâches serait inutile, car ils seraient pour la plupart identiques. Je veux explicitement séparer le modèle (peut-être un meilleur mot que le graphique de scène) et l'algorithme de dessin. – McNumber

+0

En quoi pensez-vous que l'affichage et la simulation dans un jeu diffèrent de l'affichage et de la simulation dans un autre type d'application? – SingleNegationElimination

+0

Je pense que la principale différence est la nécessité de manipuler le modèle à travers l'interface utilisateur. Cela signifie que je dois être capable de sélectionner quelque chose à l'écran et de changer son apparence. De plus, je dois dessiner le modèle de différentes manières, en fonction de l'état de l'application, de la sélection et de l'état du modèle. Par exemple, un objet sélectionné doit être mis en évidence d'une manière ou d'une autre. Alors j'ai pensé, il serait souhaitable d'avoir un objet modèle "stupide" qui est dessiné par un objet dans la couche de vue de mon application, parce que cet objet serait tout savoir sur l'état de l'application. – McNumber

0

Qu'en est-il briser l'opération d'affichage dans un ensemble d'opérations simples (se déplacer à la position (x, y), tracer une ligne de longueur N et ainsi de suite) dans le cadre d'une classe de rendu. Ensuite, chaque nœud de modèle peut avoir sa propre opération de rendu dans laquelle il appelle des opérations sur un objet de rendu qui a ces opérations simples dans son interface.

L'implémentation de la classe render peut implémenter ces opérations simples en termes d'appels Qt. La classe View de niveau supérieur appelle simplement la méthode render sur les nœuds (elle peut également passer l'objet de rendu approprié en tant qu'argument si vous ne voulez pas qu'il soit un attribut permanent de la classe de nœud).

0

Fil inactif pendant un moment mais je me demande si vous avez fait des progrès depuis et/ou travaillez toujours dans le même domaine.

J'ai un problème similaire. Je sépare une base de code existante avec tout ce qui est regroupé: Modèle (SceneGraph), GUI (win32) et rendu (OpenGL) dans un cadre de modèle/vue agréable (pas de contrôleur séparé). La séparation Modèle/Vue pour l'interface graphique fonctionne et maintenant je travaille sur le rendu OpenGL. Comme une première demande "simple" pour moi-même, j'ai commencé à restreindre le système dans le sens où aucune inclusion d'un en-tête OpenGL n'est autorisée dans la couche modèle. Cela vous oblige immédiatement à avoir un OpenGL "RenderGraph" séparé sur le côté de la vue en plus d'un SceneGraph sur le côté du modèle. Tous les nœuds possibles dans le SceneGraph obtiennent leur propre représentation de vue (comme vous l'état). Cela me plaît un peu, car les données de rendu OpenGL typiques (comme les tableaux de vertex) restent du côté de la vue. On pourrait par exemple imaginer un rendu raytrace qui n'a aucune utilité pour ces données, donc il est bon de ne pas l'inclure dans le modèle (seulement des choses comme "radius" ou "position").

La hiérarchie du SceneGraph et du Rendergraph est pour le moment similaire, bien que je puisse imaginer une structure différente basée sur des changements d'état (comme le mentionne TokenMacGuy). Lorsque le SceneGraph change (tel que déclenché par l'interface graphique, et propagé par le modèle), vous devez gérer cette notification de modification localement dans le RenderGraph. Cela implique des actions comme l'ajout/suppression/déplacement d'objets. C'est un peu lourd, mais c'est gérable.

Mon principal problème pour le moment est que la relation entre SceneGraph (Model) et le "ViewGraph" (View/GUI) est un peu différente de la relation entre SceneGraph et RenderGraph (View/OpenGL). Alors que dans le premier cas, vous pouvez avoir des connexions "horizontales" entre les nœuds du modèle et les nœuds d'affichage, dans le second cas un changement d'un nœud intermédiaire force souvent un rendu du RenderGraph entier depuis le nœud racine. Je n'ai pas trouvé une manière élégante d'incorporer cela dans mon cadre. Par exemple, supposons que vous ayez un modèle de voiture dans votre SceneGraph. Quelque part dans une feuille, vous avez "le diamètre de la valve du pneu avant gauche". Cela serait représenté dans un contrôle de texte dans le ViewGraph et lorsqu'un utilisateur le change, la feuille correspondante dans le SceneGraph est ajustée. Pas de problème, c'est un changement local. Le modèle reçoit cette modification et envoie une notification au RenderGraph pour mettre à jour le rendu OpenGL. Le noeud dans le RenderGraph recevant cette notification doit maintenant non seulement se re-renderer, mais le RenderGraph entier doit être redessiné.

Cordialement, Daniel Dekkers