Franky: Klassen-Vererbung

Hallo!

Ich habe ein (Verständnis-) Problem was die Vererbung von Methoden bei Klassen angeht.
Und zwar versuche ich eine 3D-Anwendung mit OpenGL zu schreiben.
Dazu habe ich eine Klasse "Mesh" die Methoden für z.B. den Vertex-Buffer zur Verfügung stellt.
"Mesh" wird erweitert durch die Klasse "Plane" welche mir eine 2 dimensionale Fläche erstellt - ein Rechteck. Dann habe ich mir eine Klasse "Cube" erstellt.
"Cube" erweitert "Plane" denn ich wollte mir aus 6 Planes den Cube erstellen.
Beim Ausführen des Programms, direkt nach dem Start, stürzt es aber ab.

  
public class Mesh {  
    // vertex buffer  
    private FloatBuffer vertexBuffer;  
    /* ... */  
    protected void setVertices(float[] vertices){  
        // Vertices in den Buffer schieben  
    }  
    /* ... */  
    public void draw(GL10 gl){  
        /* ... */  
        // Vertex-Buffer verwenden  
        // auf diese Zeile deutet später der Fehler  
        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);  
        /* ... */  
    }  
}  
  
public class Plane extends Mesh {  
    // Vertex-Array  
    private float[] vertices;  
    /* ... */  
    public Plane(/*...*/){  
        // Plane berechnen  
        // Koordinaten stehen in vertices  
        /*  
         * an der Stelle "funktioniert" es mit dem Aufruf von setVertices()  
         */  
        // ruft setVertices aus "Mesh" auf, schiebt Vertices in den Buffer  
        // setVertices(vertices);  
    }  
    /*  
     * wenn ich diese Funktion aufrufe, "funktioniert" es nicht  
     */  
    public void drawPlane(){  
        // ruft setVertices aus "Mesh" auf, schiebt Vertices in den Buffer  
        // damit gibt es Probleme  
        setVertices(vertices);  
    }  
}  
  
public class Cube extends Plane {  
    // eine Seite des Würfels  
    private Plane bottomPlane;  
    /* ... */  
    public Cube(/*...*/){  
        bottomPlane = new Plane(/*...*/);  
        /* mit diesem Aufruf "funktioniert" es nicht wenn setVertices  
         * in drawPlane aufgerufen wird */  
        // bottomPlane.drawPlane();  
        /* mit diesem Aufruf "funktioniert" es */  
        drawPlane();  
    }  
}  

So, was funktioniert denn nun nicht...
Ich habe eine Renderklasse in der ich mir den Würfel (Cube) erstelle.

  
public class GLRenderer implements GLSurfaceView.Renderer {  
    private Cube myCube;  
    // Constructor  
    public GLRenderer(){  
        myCube = new Cube(/*...*/);  
    }  
  
    // wird mit jedem Frame ausgeführt  
    @Override  
    public void onDrawFrame(GL10 gl){  
        /* auf diese Zeile zeigt der Fehler wenn ich  
         * bottomPlane.drawPlane(); in "Cube" aufrufe  
        myCube.draw(gl);  
    }  
}  

Also wenn ich setVertices(vertices); im Constructor von Plane aufrufe und den Aufruf von
drawPlane(); im Constructor von Cube weglasse, dann erstellt mir meine Renderklasse das Plane und ich kann es in onDrawFrame anzeigen lassen: myCube.draw(gl);

Wenn ich im Constructor von Cube aber bottomPlane.drawPlane(); aufrufe, stürzt die Anwendung mit Hinweis auf diese 2 Zeilen ab (gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);[code] und [code lang=javascript] myCube.draw(gl);). NullPointerException. Ist vertexBuffer leer??
Wenn ich aber nur drawPlane(); in Cube aufrufe, wird das Plane angezeigt.

Kann mir jemand einen Tipp geben wo ich hier in die falsche Richtung denke?
Wieso funktioniert drawPlane() aber nicht bottomPlane.drawPlane()?
Die Funktion bottomPlane.drawPlane() wird ja ausgeführt, wieso bekomme ich also eine NullPointerException?

Danke für eure Hilfe und noch einen schönen Sonntag!

  1. Tach,

    Dazu habe ich eine Klasse "Mesh" die Methoden für z.B. den Vertex-Buffer zur Verfügung stellt.
    "Mesh" wird erweitert durch die Klasse "Plane" welche mir eine 2 dimensionale Fläche erstellt - ein Rechteck. Dann habe ich mir eine Klasse "Cube" erstellt.

    Plane ist ein ungeschickter Name für dein Objekt, schließlich ist es keine zweidimensionale unbegrenzte Ebene sondern eine begrenzte Fläche.

    "Cube" erweitert "Plane" denn ich wollte mir aus 6 Planes den Cube erstellen.

    Laut deinem Quellcode, ist der Würfel eher nicht von der Fläche abgeleitet sondern hat sechs Flächen als Attribute.

    public class Cube extends Plane {
        // eine Seite des Würfels
        private Plane bottomPlane;
        /* ... /
        public Cube(/
    .../){
            bottomPlane = new Plane(/
    .../);
            /
    mit diesem Aufruf "funktioniert" es nicht wenn setVertices
             * in drawPlane aufgerufen wird /
            // bottomPlane.drawPlane();
            /
    mit diesem Aufruf "funktioniert" es */
            drawPlane();
        }
    }

      
    bottomPlane.drawPlane() setzt die vertices des Attributs bottomPlane deines "Würfels", das Atribut vertices deines Würfels wird nicht gesetzt (da drawPlane() bzw. this.drawPlane() nicht aufgerufen wird), wenn du dann in draw() auf die vertices des Würfels zugreifst, sind diese natürlich null.  
      
    Ich weiß nicht, wie OpenGL funktioniert, aber ich schätze mal die Vertices sind die Ecken eines Polygons (dann kommst du prinzipiell auch mit 4 Flächen statt mit 6 aus (und selbst dann werden zwei Kanten doppelt gezeichnet)); bei einem Drahtgittermodell würde ich vermutlich eher von 12 Kanten ausgehen als mit Flächen zu arbeiten.  
      
    mfg  
    Woodfighter
    
    1. Hallo Jens!

      Danke für deine Hilfe!

      Plane ist ein ungeschickter Name für dein Objekt, schließlich ist es keine zweidimensionale unbegrenzte Ebene sondern eine begrenzte Fläche.

      "Plane" wird aber laut Leo mit "Fläche", "Ebene" übersetzt. Insofern passt mir der Name eigentlich ganz gut!?

      Laut deinem Quellcode, ist der Würfel eher nicht von der Fläche abgeleitet sondern hat sechs Flächen als Attribute.

      Ich könnte das Attribut auch protected setzen. Wenn ich aber aus "Plane" Objekte erstellen will brauch ich zumindest die Referenz auf Plane. Das muss ich nicht zwingend als Attribut anlegen, das stimmt wohl.

      bottomPlane.drawPlane() setzt die vertices des Attributs bottomPlane deines "Würfels", das Atribut vertices deines Würfels wird nicht gesetzt (da drawPlane() bzw. this.drawPlane() nicht aufgerufen wird), wenn du dann in draw() auf die vertices des Würfels zugreifst, sind diese natürlich null.

      Also würde es funktionieren wenn ich Cube.bottomPlane.draw(gl) aufrufe?
      Klingt logisch. (grad getestet, funktioniert)
      Aber das ist nict unbedingt das was ich will, da ich den Würfel ja "im ganzen" zeichnen will. Also liegt der Fehler ist mal an der fehlerhaften Klassenerweiterung?!

      Ich weiß nicht, wie OpenGL funktioniert, aber ich schätze mal die Vertices sind die Ecken eines Polygons (dann kommst du prinzipiell auch mit 4 Flächen statt mit 6 aus (und selbst dann werden zwei Kanten doppelt gezeichnet)); bei einem Drahtgittermodell würde ich vermutlich eher von 12 Kanten ausgehen als mit Flächen zu arbeiten.

      Ja, die Vertices sind Ecken von Polygonen. Das mit den doppelten Kanten ist mir auch bereits aufgefallen. Ich tu mich aber mit der Berechnung der Vertices sehr schwer, deshalb dachte ich mir wär es einfacher einfach 2 Grundflächen zu erstellen (Rechteck, Dreieck) und daraus alle Objekte und Flächen zu konstruieren.

      Die Berechnung des Planes hab ich irgendwo im Internet gefunden allerdings hab ich sie nicht genug verstanden um daraus die Berechnung eines Würfels ableiten zu können. Der Code war leider auch nicht kommentiert.
      Vielleicht könntest du mir dabei helfen?

        
      public class Plane extends Mesh {  
      	  
      	private short[] indices;  
      	private float[] vertices;  
      	// plane with 1 segment width and 1 segment height by  
      	// 1 segment over width and height  
      	public Plane(){  
      		this(1, 1, 1, 1);  
      	}  
      	  
      	 // let you decide the size of the plane but still only one segment  
      	public Plane(float width, float height){  
      		this(width, height, 1, 1);  
      	}  
      	  
      	// setting up the plane with different settings  
      	public Plane(float width, float height, int widthSegments, int heightSegments){  
      		// calculate the buffers  
      		vertices = new float[(widthSegments + 1) * (heightSegments + 1) * 3];  
      		indices = new short[(widthSegments + 1) * (heightSegments + 1) * 6];  
      		  
      		float xOffset = width / -2;  
      		float yOffset = height / -2;  
      		float xWidth = width / (widthSegments);  
      		float yHeight = height / (heightSegments);  
      		  
      		int currentVertex = 0;  
      		int currentIndex = 0;  
      		int x;  
      		int y;  
      		int n;  
      		  
      		short w = (short) (widthSegments + 1);  
      		  
      		// each columns with each rows  
      		for (y = 0; y < heightSegments + 1; y++) {  
      			for (x = 0; x < widthSegments + 1; x++) {  
      				vertices[currentVertex] = xOffset + x * xWidth;  
      				vertices[currentVertex + 1] = yOffset + y * yHeight;  
      				vertices[currentVertex + 2] = 0;  
      				  
      				currentVertex += 3;  
      				  
      				n = y * (widthSegments + 1) + x;  
      				  
      				if (y < heightSegments && x < widthSegments) {  
      				    // face one  
      				    indices[currentIndex] = (short) n;  
      				    indices[currentIndex + 1] = (short) (n + 1);  
      				    indices[currentIndex + 2] = (short) (n + w);  
      				    // face two  
      				    indices[currentIndex + 3] = (short) (n + 1);  
      				    indices[currentIndex + 4] = (short) (n + 1 + w);  
      				    indices[currentIndex + 5] = (short) (n + 1 + w - 1);  
      		  
      				    currentIndex += 6;  
      				}  
      			}  
      		}  
      		//setIndices(indices);  
      		//setVertices(vertices);  
      	}  
      	// draw the plane  
      	public void DrawPlane(){  
      		setIndices(indices);  
      		setVertices(vertices);		  
      	}  
        
      }  
      
      

      Was passiert ist, dass ein Rechteck mit X Segementen (je 2 Dreiecke) pro Breite und Höhe gezeichnet wird. Z.B. ein Rechteckt mit der Größe 4*5 und 4 Segmente für Breite und 5 Segmente für die Höhe: new Plane(4f, 5f, 4, 5);
      Was die Berechnung angeht, scheitert mein Verständnis bereits bei "// calculate the buffers".
      Wieso werden die Segmente um 1 erhöht und wieso *3 bzw *6?
      Die weiteren Schritte der Berechnung erschließen sich mir leider auch nicht.

      Danke für Hilfe!

      1. Tach,

        "Plane" wird aber laut Leo mit "Fläche", "Ebene" übersetzt. Insofern passt mir der Name eigentlich ganz gut!?

        in der Geometrie ist eine Plane eindeutig eine Ebene, du willst eher eine Area oder ein Rectangle, wenn der erste Blick, den ich auf den Quelltext unten geworfen habe.

        Laut deinem Quellcode, ist der Würfel eher nicht von der Fläche abgeleitet sondern hat sechs Flächen als Attribute.

        Ich könnte das Attribut auch protected setzen.

        Private tut es auch, üblicherweise nutzt man Getter/Setter um auf die Attribute von Objekten zuzugreifen.

        Wenn ich aber aus "Plane" Objekte erstellen will brauch ich zumindest die Referenz auf Plane. Das muss ich nicht zwingend als Attribut anlegen, das stimmt wohl.

        Das kann der Würfel selber tun.

        Aber das ist nict unbedingt das was ich will, da ich den Würfel ja "im ganzen" zeichnen will. Also liegt der Fehler ist mal an der fehlerhaften Klassenerweiterung?!

        Sie macht es zumindest unverständlicher: Ich würde, wie gesagt den Würfel mit sechs Attributen vom Typ Plane definieren (alternativ eine Collection, die die sechs Planes enthält), der Konstruktor von Cube setzt dann die Attribute und deren Eigenschaften (bevorzugt über den Konstruktor) und wenn die draw()-Methode des Würfels aufgerufen wird, dann ruft dieser die draw()-Methoden der Flächen auf (es geht dir ja beim Aufruf darum den Würfel zu zeichnen, der Würfel kümmert sich dann quaasi um die Details).

        Die Berechnung des Planes hab ich irgendwo im Internet gefunden allerdings hab ich sie nicht genug verstanden um daraus die Berechnung eines Würfels ableiten zu können. Der Code war leider auch nicht kommentiert.
        Vielleicht könntest du mir dabei helfen?

        Nicht mehr heute nacht, vielleicht schaue ich morgen nochmal drauf.

        mfg
        Woodfighter

        1. Guten Morgen!

          Sie macht es zumindest unverständlicher: Ich würde, wie gesagt den Würfel mit sechs Attributen vom Typ Plane definieren[..]

          Also doch 6 planes und kein Gitternetz-Model? Der "saubere Weg" des Gitternetzes würde mir mittlerweile viel besser gefallen denn ich hab mir überlegt, dass sich doppelte Punkte irgendwann summieren was wesentlich mehr Rechenaufwand beim rendern bedeuten würde.

          Ich versuche gerade die Anzahl der Vektoren eines Würfels zu berechnen deren Flächen aus mehreren Segmenten bestehen.

          Wenn die Flächen nur aus 1 Segment bestehen ist die Formel einfach: 2^3 = 8
          Wenn die Flächen aber z.b. aus je 4 Segmenten bestehen (Breite, Höhe und Tiefe je 2 Segmente also 2 Dreiecke pro Segment) habe ich 26 Vektorpunkte.
          Das kann man noch einfach zählen, aber wie ist die Formel dafür?
          Daran zerbrech ich mir grad den Kopf. Und wenn ich erstmal die Anzahl habe, muss ich mir Gedanken machen wie ich die Positionen berechnen kann.