Computergrafik

Szenengraph Traversierung

Szenengraph Implementierung | | Szenengraph Klassengraph

Wie wird nun aber insbesondere dieser Szenengraph-Traversal implementiert, so dass er für die jeweiligen traversierten Geometrie-Knoten die jeweils passende Matrix “ausspuckt”?

Er muss dazu die die passende Abfolge an Transformationsänderungen und Render-Calls veranlassen:

Erklärung in 5 Schritten:

1) Zuersteinmal hat die Basisklasse lgl_Node aller Knoten als Instanzvariable ein dynamisches Array, in dem die hinzugefügten Kinder gespeichert sind. Das dynamische Array wird hierbei als std::vector in der Basisklasse aggregiert:

protected:
std::vector<lgl_Node*> children_;

2) Die Basisklasse hat weiterhin eine virtuelle Methode render(), welche sich rekursiv für alle Kinder aufruft, was einer Tiefensuche entspricht:

virtual void render()
{
   for (unsigned int i=0; i<children_.size(); i++)
      children_[i]->render();
}

3) Ein Geometrie-Knoten (lgl_GeometryNode), der von der Basisklasse abgeleitet ist, rendert nun bei der obigen Traversierung den gekapselten und als Instanzvariable gespeicherten VBO, in dem er die virtuelle Methode spezialisiert:

class lgl_GeometryNode: public lgl_Node
{
public:

   //! ctor
   lgl_GeometryNode(lglVBO *vbo = NULL)
      : lgl_Node(),
        vbo_(vbo)
   {}

virtual void render()
{
   if (vbo_)
   {
      vbo_->lglRender();
   }

   lgl_Node::render();
}

protected:

   lglVBO *vbo_;
};

4) Die dazu passenden Transformationen werden dadurch berechnet, dass ein Transformations-Knoten (lgl_TransformationNode) wiederum die virtuelle Methode spezialisiert und die aktuell gespeicherte lokale Transformationsmatrix M anwendet:

virtual void render()
{
   lglPushMatrix();
   lglMultMatrix(M);

   lgl_Node::render();

   lglPopMatrix();
}

5) Hinzugefügte Knoten müssen nicht manuell gelöscht werden, da sie durch das sog. ref-counting automatisch gelöscht werden, wenn z.B. der Szenengraph gelöscht wird. Dies gilt nicht für die gekapselten VBOs. Die Implementierung der Methode add() der Basisklasse, welche ref-counting implementiert, sieht daher so aus:

template <class T>
T *add(T *node)
{
   if (lgl_Node *child=dynamic_cast<lgl_Node*>(node))
   {
      children_.push_back(node);
      node->refcount_++;
      return(node);
   }

   return(NULL);
}

Und der Destruktor der Basisklasse kümmert sich darum, dass automatisch alle Kinder und Kindeskinder gelöscht werden, wenn der Knoten selber gelöscht wird:

virtual ~lgl_Node()
{
   for (unsigned int i=0; i<children_.size(); i++)
   {
      children_[i]->refcount_--;
      if (children_[i]->refcount_ == 0)
         delete children_[i];
   }

Die hier skizzierte Implementierung eines Szenengraphen ist vom Prinzip her auf alle Animations-Tools wie Blender oder Game-Engines wie Unreal, Unity oder Godot, die alle auf die eine oder andere Weise ebenfalls das Szenengraph-Prinzip einsetzen, übertragbar!

qed!

Szenengraph Implementierung | | Szenengraph Klassengraph

Options: