Scenegraph mit unterschiedlichen Nodes und dessen Funktionen



  • Dein Beispiel ist schon sehr komisch. In der Basisklasse hast du keine custom Funktion angegeben, dann heißt es plötzlich custom override, und dann customtwo. Wenn du mit dem ganzen irgendwas sagen wolltest, ist es nicht rübergekommen.



  • Mechanics schrieb:

    Dein Beispiel ist schon sehr komisch. In der Basisklasse hast du keine custom Funktion angegeben, dann heißt es plötzlich custom override, und dann customtwo. Wenn du mit dem ganzen irgendwas sagen wolltest, ist es nicht rübergekommen.

    Du hast einfach mal alles völlig falsch verstanden. Ich überschreibe custom nicht, sondern habe nur das Kommentar dort angebracht wo die eigentlichen virtuellen Funktionen überschrieben hätten müssen um mir die Schreibarbeit zu sparen weil sie für das Beispiel irrelevant sind. Es geht mir darum Klassen von der Basisklasse abzuleiten und denen spezielle Funktionen zuzuschreiben die eben nicht in der Basisklasse existieren und diese dann mit einer foreach schleife aufrufen zu können.



  • beschreib doch mal, wieso du glaubst, das zu brauchen...
    falls es nur ums funktionieren geht und du base verändern kannst:

    virtual bool is_derived_with_custom_fct() const { return false; }
    bzw return true; in derived

    und vor dem blinden static_cast einfach prüfen



  • Ich möchte einen Scenegraph erstellen wo verschiedene Arten von Nodes enthalten sind. Anschließend möchte ich diesen Scenegraph runter und rauf rattern und mit jeder Node etwas bestimmtes machen. http://de.wikipedia.org/wiki/Szenengraph

    z.B. ist eine Node eine "Transformationsnode" wo eine Matrix enthalten ist die ich an das Rendersystem pushen will und eine andere Node z.B. die Modelnode soll einen VBO an OpenGL oder DirectX pushen. Jeweils andere funktionen.. manche sollen auch nichts machen je nach dem und da es bei einem Szenengraphen einfach unpraktisch und unlogisch ist für jede Art von Node nen neuen Baum zu machen sollen die alle in den gleichen.



  • Extremo schrieb:

    Es geht mir darum Klassen von der Basisklasse abzuleiten und denen spezielle Funktionen zuzuschreiben die eben nicht in der Basisklasse existieren und diese dann mit einer foreach schleife aufrufen zu können.

    Das hab ich schon verstanden. Aber dein Beispiel war eben schlecht und eher irritierend als hilfreich.

    Schau dir OpenSceneGraph an. Die machen viel mit dem Visitor Pattern, kann man wenn ich mich recht erinnere auch zum Suchen von Knoten verwenden. Außerdem gibts glaub ich sowas wie asXyz Funktionen für die wichtigen Knotentypen.



  • @Extremo
    Dein Beispiel ist völlig unverständlich und falsch. (z.B. schonmal syntaktisch falsch, und wenn man davon ausgeht dass du keinen KOMPLETTEN Topfen programmiert hast, dann ist das "returns 1337 twice" auch gänzlich unmöglich, da du nur ein Child hinzufügst.)
    Damit wirst du also keine brauchbaren Antworten bekommen.

    Extremo schrieb:

    z.B. ist eine Node eine "Transformationsnode" wo eine Matrix enthalten ist die ich an das Rendersystem pushen will und eine andere Node z.B. die Modelnode soll einen VBO an OpenGL oder DirectX pushen. Jeweils andere funktionen.. manche sollen auch nichts machen je nach dem und da es bei einem Szenengraphen einfach unpraktisch und unlogisch ist für jede Art von Node nen neuen Baum zu machen sollen die alle in den gleichen.

    Ich glaube du hast nicht verstanden wie man einen Scene-Graph typischerweise implementiert.

    Generell wirst du da sowas finden wie

    class Node
    {
    // ...
        void Update()
        {
            UpdateDownwardPass();
            for (auto& child : m_children)
                child->Update();
            UpdateUpwardPass();
        }
    
        virtual void UpdateDownwardPass() {}
        virtual void UpdateUpwardPass() {}
    };
    

    und das selbe dann nochmal für Render() .
    Und in UpdateDownwardPass/UpdateUpwardPass bzw. RenderDownwardPass/RenderUpwardPass * machen die Nodes dann was sie halt zu machen haben.
    Und evtl. wird bei Update/Render noch ein UpdateContext/RenderContext mitgegeben, worüber die Node dann auf z.B. den aktuellen Timestamp auslesen kann oder auf den Renderer zugreifen.

    ps: Dass hier für jedes Child DerivedOne::custom aufgerufen wird liegt schlicht und einfach daran dass du ... für jedes Child DerivedOne::custom aufrufst. Was erwartest du dir von einem static_cast + darauffolgenden Aufruf einer nicht-virtuellen Funktion? => Bitte 1x C++ Grundlagen lernen.
    (Bzw. genaugenommen hat das Programm UB, da du DerivedOne::custom auf Objekte aufrufst die gar kein DerivedOne sind. Was nicht erlaubt ist. Du musst dich allerdings selbst darum kümmer dass du das nicht machst, C++ hilft dir hier nicht magisch aus indem es den verbotenen Aufruf dann einfach weglässt oder sowas.)

    *:
    Bin mir nicht sicher ob es üblich ist das Rendern in RenderDownwardPass/RenderUpwardPass zu splitten. Vermutlich nicht. Ist aber auch egal, wenn es nötig ist kommt man während der Entwicklung schon drauf, und splittet es dann halt entsprechend.



  • hustbaer schrieb:

    @Extremo
    Dein Beispiel ist völlig unverständlich und falsch. (z.B. schonmal syntaktisch falsch, und wenn man davon ausgeht dass du keinen KOMPLETTEN Topfen programmiert hast, ....

    Mir ist vollkommen klar das man normalerweise ein post und pre-render darin stehen hat usw. Mir geht es auch nicht darum wie es im Standardfall implementiert wird, sondern wie ich es in diesem speziellen Fall implementieren könnte.

    Ja, stimmt. Ich füge nur ein Child hinzu. Natürlich mein Fehler bei dem Beispiel. Allerdings ist sonst soweit alles in Ordnung und ist sogar in fast der gleichen Form in meinem eigenen Programm vorhanden.

    Das Problem mit dem Aufruf war eben das entscheidende. Ich habe schon verstanden das beim static_cast beim kompilieren eben genau diese auf eine andere Klasse gecastet wird und somit die Funktion zweimal aufgerufen wird. Was mir eben wichtig war ist das sie nur bei auch tatsächlich übereinstimmenden Klassen aufgerufen wird, die vorher auch bereits diese bestimmte derived Klasse waren oder eben nicht. Das ist übrigens mit dynamic_cast und if(pointer) sehr schön zu lösen.

    Beim Rendern wird gesplittet, weil z.B. transformationen aufgehoben werden müssen usw.

    Allerdings beantwortet nichts davon meine Frage. Wäre es möglich einen Szenengraphen zu implementieren, der eben nicht nur eine generische Update(post/pre) und Render(post/pre) Funktion beinhaltet?

    Ich bin euch ja allen sehr dankbar das ihr immerhin die Zeit genommen habt mir zu antworten, aber solche Antworten die irgendwie an meinem Beispiel rumnörgeln oder mich dazu aufweisen die C++ Grundkentnisse zu lernen helfen mir nicht weiter. Ich bin mir den Grundkentnissen schon bewusst und es fehlt mir hier nicht an Implementationsmöglichkeiten die um das Problem herumspielen. Ich würde aber gerne wissen ob genau DIESE Implementation nach der ich in diesem Thread frage möglich wäre. Es tut mir wirklich leid wenn ich nur ein Child hinzugefügt habe oder mein Beispiel nicht sehr ersichtlich ist. Ich fand es eindeutig, aber wahrscheinlich auch aus dem einfachen Grund das ich mit meinem Problem bekannt bin.

    Naja, wie dem auch sei. Beim nächsten mal werde ich versuchen ein deutlicheres Beispiel zu erarbeiten und hoffe dann auch auf bessere Antworten.



  • In Ordnung ist gut, da fehlen ein paar Leerzeichen und Strichpunkte. Wobei die zugegebenermassen zum Verständnis nicht nötig sind. 😉

    Allerdings beantwortet nichts davon meine Frage. Wäre es möglich einen Szenengraphen zu implementieren, der eben nicht nur eine generische Update(post/pre) und Render(post/pre) Funktion beinhaltet?

    Gegenfrage:
    Angenommen du hättest eine automagische "für alle Children vom Typ X" Funktion.
    Was würdest du damit machen wollen?
    Und was für einen Vorteil würdest du dir davon erwarten?

    Ich frag' das jetzt nicht um dich zu nerven - sondern ich weiss wirklich nicht worauf du hinaus willst.
    Speziell da dir ja anscheinend Lösungsmöglchkeiten für die ursprünglich gestellte Frage bekannt sind.

    Bzw. falls es dir wirklich nur darum geht - was mir so auf die Schnelle einfällt:

    • dynamic_cast - hast du ja selbst schon erwähnt (ist aber vergleichsweise langsam)
      * double dispatch (Visitor pattern) (schon schneller, aber auch net optimal)
      * selbstgestrickte Type-Info (z.B. einfach int m_typeId ) (geht nur wenn man immer nur auf den "most derived type" filtern will)
      * Bitmaske wo für jede benötigte "Update-Art" einer Node ein Bit gesetzt ist (sollte mMn. ziemlich flott laufen)
      * Eigene Liste pro Update-Art (erhöhter Wartungsaufwand, d.h. langsamer beim Einfügen/Löschen neuer Nodes, dafür maximal schnell zu durchlaufen)

    ps:

    aber solche Antworten die irgendwie an meinem Beispiel rumnörgeln oder mich dazu aufweisen die C++ Grundkentnisse zu lernen helfen mir nicht weiter

    Ich finde schon dass die weiterhelfen, du schreibst ja selbst

    Ich fand es eindeutig, aber wahrscheinlich auch aus dem einfachen Grund das ich mit meinem Problem bekannt bin.

    Naja, wie dem auch sei. Beim nächsten mal werde ich versuchen ein deutlicheres Beispiel zu erarbeiten und hoffe dann auch auf bessere Antworten.



  • hustbaer schrieb:

    Angenommen du hättest eine automagische "für alle Children vom Typ X" Funktion.
    Was würdest du damit machen wollen?
    Und was für einen Vorteil würdest du dir davon erwarten?

    Vielleicht macht es aus der technischen Perspektive nicht unbedingt viel Sinn. Schließlich könnte ich ja genau so gut alles in eben diesen vordefinierten Methoden ausführen. Allerdings find ich das es der Lesbarkeit enorm beiträgt und auch die Wartung vereinfacht. Das einzige Plus was mir zur Performance dabei einfällt ist schlicht die Tatsache, manche Sprünge in andere Methoden zu vermeiden, da diese eben überhaupt gar nichts bei den genannten Methoden machen müssen. Es ging mir hier wirklich lediglich darum, es deutlicher darzustellen.

    hustbaer schrieb:

    * double dispatch (Visitor pattern) (schon schneller, aber auch net optimal)

    * Eigene Liste pro Update-Art (erhöhter Wartungsaufwand, d.h. langsamer beim Einfügen/Löschen neuer Nodes, dafür maximal schnell zu durchlaufen)

    Könntest du mir vielleicht diese beiden ein wenig genauer erläutern? Mit den anderen Methoden bin ich bereits vertraut. Außer du meinst das ich für jede Node einen Vektor anlege der diese ablegt. Das möchte ich gerne vermeiden, um eben nicht mehrere "Bäume" bzw. Hierarchien zu erstellen.



  • Das Visitor-Pattern ist denke ich ausreichend dokumentiert (Wikipedia etc.).

    Und mit der "eigene Liste pro Update-Art" meine ich bloss die "Materialisierung" der "benötigt XYZ Update" Flags als eigene Listen.
    Also ja, dazu bräuchte dann jede Node eine eigene Liste von Children die z.B. gerendert werden müssen, eine eigene Liste von Children die ein "Logic Update" brauchen usw.
    (Bzw. man könnte es auch als eine einzige sortierte Liste implementieren in der man dann mehrere Range-Scans machen muss. Bzw. auch als B-Baum. Ich glaube aber nicht dass diese Variante so "gut" ist dass sich der Aufwand für die sortierte Liste/B-Baum Implementierung lohnen würde.)

    Und das geht dann schon ganz leicht in Richtung Component based Design -- wo der Scene Graph dann weitestgehend degradiert wird (oder ganz entfernt). Der wird dann eigentlich nur benötigt falls eine Baumartige Hierarchie der Geometrie. Was oft nicht der Fall ist (bzw. nur lokal benötigt wird, also innerhalb von einzelnen Objekten, damit der Fuss halt am Bein dranhängt).

    ps:

    Allerdings find ich das es der Lesbarkeit enorm beiträgt und auch die Wartung vereinfacht.

    Ich kann mir grad nicht vorstellen wie das aussehen könnte so dass es die Lesbarkeit erhöht. Hmmm.


Anmelden zum Antworten