Designfrage



  • @brotbernd: ITexture implementiert Methoden wie Fill, Rotate, Load. Diese Funktionen bietet die AnimatedTexture nicht, weil sie ja mehr oder weniger nur ein Container für ITexturen ist.

    @Tachyon: Ich möchte zB aber bei einer PictureBox im Gui einfach der Image Eigenschaft eins von beidem zuweisen. Bisher ist dort Image als shared_ptr<ITexture> implementiert. Ich möchte aber sowas machen können:
    [cpp]pictureBox->Image = statische Texture oder animierte Textur[/cpp]

    @dot: Je nach verwendeter Technik gibt es natürlich noch OpenGLTexture, D3D10Texture etc und jede Technik hat ihren eigenen Renderer. Der OpenGLRenderer castet ITexture nach OpenGLTexture etc. Intern wird alles einheitlich über das IRenderer Interface behandelt, weil dort noch nicht klar ist, welche Technik der Benutzer letztendlich einsetzt.

    greetz KN4CK3R



  • WIe wäre es mit einem Visitor:

    struct abstract_render_engine
    {
        virtual void render(d3d9_texture texture) = 0;
        virtual void render(d3d9_animated_texture texture) = 0;
    
        virtual void render(ogl_texture texture) = 0;
        virtual ~abstract_render_engine();
    };
    
    struct abstract_texture
    {
        virtual void render(abstract_render_engine engine) = 0;
        virtual ~abstract_texture();
    };
    
    class d3d9_texture : public abstract_texture
    {
        render(abstract_render_engine engine){ engine.render(*this); }
    };
    
    class d3d9_animated_texture : public abstract_texture
    {
        render(abstract_render_engine engine){ engine.render(*this); }
    };
    
    class d3d9_renderer : public abstract_render_engine
    {
        virtual void render(d3d9_texture texture)
        {
            //do things with texture
        }
    
        virtual void render(d3d9_animated_texture texture)
        {
            //do things with animated texture
        }
    
        virtual void render(ogl_texture texture)
        {
            throw texture_not_supported();
        }
    };
    

    ? Irgendwie so.

    Mit einer abstrakten Fabrik könntest Du dann noch gewährleisten, dass man zu einem Renderer nur die passenden Texturen etc. erzeugt werden können.



  • KN4CK3R schrieb:

    @Tachyon: Ich möchte zB aber bei einer PictureBox im Gui einfach der Image Eigenschaft eins von beidem zuweisen. Bisher ist dort Image als shared_ptr<ITexture> implementiert. Ich möchte aber sowas machen können:

    Dann muss Image eben beide Typen annehmen (Überladung). Wenn Du Sachen nicht einheitlich behandeln kannst, dann musst Du irgendwo separate Behandlungen dafür bereitstellen. Das kannst Du entweder suaber von Anfang an machen oder irgendwo in den Eingeweiden mit Hacks wie dynamic_Cast, die hier aber eigentlich überflüssig wären.



  • @brotbernd: nein, der bin ich nicht.

    @Tachyon: ist das nicht meine Möglichkeit 3 mehr oder weniger?

    Ich habe mich vom Design her an dem .NET Framework orientiert. Dort hat eine PictureBox die Eigenschaft Image, die sowohl normale Grafiken als auch animierte verwalten kann. Wie genau die Klasse (System.Drawing.Image) das handhabt, weiß ich nicht, dazu schaue ich mir eventuell deren Quellcode später einmal an. Ich nehme an so wie bei mir in Möglichkeit 1.

    greetz KN4CK3R



  • Ja, eine Lösung über Double Dispatch wäre zumindest sehr viel sauberer.
    Aber rein prinzipiell versuchst du da einen API-Wrapper zu basteln. Und meiner Erfahrung nach ist das keine besonders gute Idee. Glaub mir, wo ich noch klein war, hab ich x Mal versucht, genau auf Basis eines solchen Design meine 42ste "Engine" (diesmal klappts bestimmt) zu bauen.
    Versuch nicht was zu basteln, was nach Außen wieder gleich aussieht wie die Grafik-API drunter. Dadurch gewinnst du nichts. Bastel dir eine vernünftige Abstraktion für deine Anwendung. Was genau will deine Anwendung tun? Davon, dass es auf API-Ebene sowas wie "Texturen" gibt, muss deine Anwendung normalerweise überhaupt nichts wissen...



  • Ist ein ITexture Interface, dass Methoden zum Bearbeiten von Bitmapdaten anbietet kein vernünftige Abstraktion? Genauer ins Detail gehen möchte ich an dieser Stelle ja nicht, weil für alles weitere eben die Grafik-API zuständig ist.

    Insgesamt läuft es also auf eine Basisklasse hinaus, von der beide Versionen erben, was aber irgendwie wieder auf das Get in Möglichkeit 3 hinausläuft, wenn ich meiner AnimatedTexture nicht die Möglichkeit gebe, sich wie eine ITexture zu verhalten (Fill-Methoden, etc) Damit könnte ich ja sogar noch leben, indem sie zB eine Methode SelectFrame(frameNum) bekommt und dann die Methoden nur auf dieses eine Frame ausführt. :|

    greetz KN4CK3R



  • KN4CK3R schrieb:

    @Tachyon: ist das nicht meine Möglichkeit 3 mehr oder weniger?

    Eher weniger.



  • KN4CK3R schrieb:

    Ist ein ITexture Interface, dass Methoden zum Bearbeiten von Bitmapdaten anbietet kein vernünftige Abstraktion?

    Die Tatsache dass du diesen Downcast in RenderTexture brauchst, schreit doch nach Designfehler.

    Was genau ist es, das deine Anwendung tut und warum muss es dabei unbedingt Renderer->RenderTexture() sein und nicht z.B. Texture->Render() ?



  • den Downcast brauche ich, damit ich an das eigentliche LPDIRECT3DTEXTURE9 Objekt (in D3D9Texture) komme, das mir das IDirect3DDevice9 (in D3D9Renderer) dann zeichnet.

    class D3D9Renderer : public IRenderer
    {
    public:
    	virtual void RenderTexture(shared_ptr<ITexture> texture)
    	{
    		shared_ptr<D3D9Texture> temp = static_pointer_cast<D3D9Texture>(texture);
    
    		device->SetTexture(0, temp->GetTexture());
    
    		//ob ich hier jetzt in D3D9Texture oder einen Schritt später GetTexture() in LPDIRECT3DTEXTURE9 caste, ist ja egal...
    
    		AddVertex(x, y, 0.0f, 0.0f);
    		AddVertex(x + w, y, 1.0f, 0.0f);
    		AddVertex(x, y + h, 0.0f, 1.0f);
    		AddVertex(x + w, y, 1.0f, 0.0f);
    		AddVertex(x + w, y + h, 1.0f, 1.0f);
    		AddVertex(x, y + h, 0.0f, 1.0f);
    
    		device->DrawPrimitiveUP(D3DPT_TRIANGLELIST, verticesNum / 3, &vertices[0], sizeof(Vertex2D));
    	}
    };
    

    Die ganze Vertex Verwaltung hat in der Textur nichts zu suchen. IDirect3DDevice9 rendert ja die Textur und nicht umgekehrt. Aus diesem Grund gibt es auch keine Textur->Render() Funktion (klar, sorum wäre das ganze absolut unproblematisch)

    greetz KN4CK3R



  • Ich greife nochmal meinen Code auf:

    virtual void render(d3d9_texture texture)
        {
            device->SetTexture(0, texture->GetTexture()); //kein cast
            AddVertex(x, y, 0.0f, 0.0f);
            AddVertex(x + w, y, 1.0f, 0.0f);
            AddVertex(x, y + h, 0.0f, 1.0f);
            AddVertex(x + w, y, 1.0f, 0.0f);
            AddVertex(x + w, y + h, 1.0f, 1.0f);
            AddVertex(x, y + h, 0.0f, 1.0f);
    
            device->DrawPrimitiveUP(D3DPT_TRIANGLELIST, verticesNum / 3, &vertices[0], sizeof(Vertex2D));         
        }
    


  • den cast für eine Renderer Klasse einzusparen, die für jeden beliebigen Texturtyp eine RenderMethode bereitstellt, halte ich für etwas fragwürdig. Wenn eine neue Grafik-API dazukommt, muss jeder Renderer (oder nur der BaseRenderer, falls er nicht mehr virtuel gemacht wird) geändert werden, nur um in der Funktionen eine "gibts net" Exception zu werfen. Das kann unmöglich ein gutes Design sein.

    greetz KN4CK3R



  • KN4CK3R schrieb:

    den cast für eine Renderer Klasse einzusparen, die für jeden beliebigen Texturtyp eine RenderMethode bereitstellt, halte ich für etwas fragwürdig. Wenn eine neue Grafik-API dazukommt, muss jeder Renderer (oder nur der BaseRenderer, falls er nicht mehr virtuel gemacht wird) geändert werden, nur um in der Funktionen eine "gibts net" Exception zu werfen. Das kann unmöglich ein gutes Design sein.

    greetz KN4CK3R

    Doch ist es. Denn jeder neue Renderer und die dazu gehörenden Texuren ist unterschiedlich, und das wird dadruch ausgedrückt. Du hingegen versuchst die ganze Zeit, Unterscheide auf "Teufel komm raus" gleich zu biegen. Und das ist scheiss Design. Zu mal die Anzahl der möglichen Renderer vermutlich eher begrenzt sein wird.



  • der Unterschied zwischen OpenGL und D3D Textur tritt im entsprechenden Renderer auf, vorher existiert kein Unterschied. Und der Renderer weiß, was er vor sich hat.

    Ein Autohändler stellt ja auch nur ein Schild "kaufe Kraftfahrzeuge" und nicht "kaufe PKWs", "kaufe Motorräder", "kaufe LKWs" vor sein Geschäft und sagt dem LKW Fahrer dann "ich kaufe ihren LKW aber nicht ihren PKW und auch nicht ihr Motorrad" eben weil der LKW Fahrer nur einen LKW hat.

    greetz KN4CK3R



  • KN4CK3R schrieb:

    den Downcast brauche ich, damit ich an das eigentliche LPDIRECT3DTEXTURE9 Objekt (in D3D9Texture) komme, das mir das IDirect3DDevice9 (in D3D9Renderer) dann zeichnet.

    Mir ist klar warum du den Cast brauchst. Das Problem liegt ja eben genau in der Natur dieses Designs, das den Cast erst notwendig macht. Der Cast ist nur Symptom. Worum es geht ist, mal festzustellen, dass hier ein Designfehler vorliegt.

    KN4CK3R schrieb:

    Die ganze Vertex Verwaltung hat in der Textur nichts zu suchen. IDirect3DDevice9 rendert ja die Textur und nicht umgekehrt. Aus diesem Grund gibt es auch keine Textur->Render() Funktion (klar, sorum wäre das ganze absolut unproblematisch)

    Und was interessiert das die Anwendung, dass es intern ein ID3Direct3DDevice9 gibt, das die Textur rendert? Warum muss deine Abstraktionsschicht unbedingt das Interface der zugrundeliegenden Grafik-API widerspiegeln? In D3D11 hast du plötzlich keinen Device mehr, der rendert, sondern einen DeviceContext. Deine D3D11 Implementierung möchte vielleicht multithreaded Rendering einsetzen. Wie löst du das? Ich vermute mal, deine Anwendung will einfach nur 2D Bildchen malen!? Dann bau ein Interface, dass es erlaubt, 2D Bildchen zu malen. Und zwar so wie die Anwendung es will und nicht so wie die Grafik-API es will.



  • ja macht die Anwendung doch?! Die Anwendung ruft beim Renderer RenderTexture auf, alles was danach passiert, ist Sache der Grafik-API und interessiert die Anwendung nicht. Die Anwendung kann sich ja auch gar nicht dafür interessieren, weil sie nicht weiß, auf welcher Grafik-API sie überhaupt ausgeführt wird.

    greetz KN4CK3R



  • Warum genau muss die Anwendung ihre Texturen selbst rendern. Sollte das Rendern nicht vielleicht Aufgabe des Renderers sein? Wenn es sich da um einen Sprite Renderer handelt (was ich mal schwer vermute), dann wirst du früher oder später nicht eine Textur pro Bild haben, sondern mehrere Bilder in eine Textur packen wollen, um möglichst viele Sprites in einem Drawcall zu rendern. Wie löst du das?



  • Wieso sollte sie sich nicht selbst zeichnen?
    Die PictureBox bekommt quasi per WM_PAINT einen IRenderer* geschickt und ruft dann renderer->RenderTexture(mein anzuzeigendes Bild); auf. Woher sollte der Renderer wissen, wie eine PictureBox aussieht.

    Eine Textur beinhaltet nur ein Bild (außer die animierte ;)) und dadran wird sich auch nichts ändern. Dass alles auf einer Textur ist und nur gezeichnete Ausschnitt verschoben wird, ist nicht geplant und auch nicht gewollt.

    greetz KN4CK3R



  • KN4CK3R schrieb:

    Ein Autohändler stellt ja auch nur ein Schild "kaufe Kraftfahrzeuge" und nicht "kaufe PKWs", "kaufe Motorräder", "kaufe LKWs" vor sein Geschäft und sagt dem LKW Fahrer dann "ich kaufe ihren LKW aber nicht ihren PKW und auch nicht ihr Motorrad" eben weil der LKW Fahrer nur einen LKW hat.

    Ein PKW Händler stellt aber das Schild auf "Kaufe PKW" und ein LKW Händler stellt das Schild auf "Kaufe LKW". Denn ein PKW Händler will nur PKW haben und ein LKW Händler nur LKW. Ein Kraftfahrzeugshandel ist ein abstraktes Gebilde, genau wie ein Kraftfahrzeug. Wenn es kein generelles Konzept gibt, dass jeder Kraftfahrzeughandel ein beliebiges Kraftfahrzeug kaufen kann, dann gibt es kein Schild "Kaufe Kraftfahrzeug", dass vor jedem Kraftfahrzeughandel steht.

    Es ist das gleiche wie hier, eine Verletzung des LSP. Das Schild "Kaufe Kraftfahrzeuge" ist für den PKW Handel mit einer zusätzlichen Bedingung versehen, der PKW Händler sagt "Ätsch.. kaufe gar nicht alle Kraftfahrzeuge, ich kaufe nur PKW". Du kannst dem PKW Händler nicht einfach statt einem PKW ein Motorrad verkaufen (Substitution), da wird es irgendwo Ärger geben.


Anmelden zum Antworten