Noch einmal Matrizen

Hey Leute.

Ich hab mich grad nochmal mit Matrizen beschäftigt, und gemerkt das ich hier und da immer noch
ein paar verständnigssprobleme hab.

Mit folgendem Code rotiere ich ein Objekt, dessen Verticies im Intervall [0-1] sind um seinen Mittelpunkt, (Um seine
eigene Y achse) und bewege es entlang der globalen x achse.

		float step = AGLRenderController.getDeltaS() * 0.5f;
		
		quad.getModel().setIdentity();
		quad.getModel().scale(new Vector3f(10, 10, 10));
		
		quad.getModel().translate(new Vector3f(r / 90, 0, 0));
		quad.getModel().rotate((float) Math.toRadians(r), new Vector3f(0, 1f, 0f));
		quad.getModel().translate(new Vector3f(-0.5f, 0, 0));
		
		
		r += step * 90;

Jetzt will ich das gleiche machen, aber ohne immer wieder setIdentity() aufzurufen. also eine matrix haben, die ich nur bearbeite
damit oberes entsteht. ich bekomm das nicht hin.

Warum ich das machen will, schreib ich später nochmal, hab grad keine zeit…

Jemdn eine idee?

Du brauchst die Bewegung (also Translation und Rotation) eigentlich nur in eine weitere Matrix packen und diese dann mit der Model-Matrix zu multiplizieren. Das Model sollte dafür vorher gescaled werden und r ergibt sich bei jedem Durchlauf aus “step*90” (und muss demzufolge auch nicht pro Durchlauf aufaddiert werden).

ja und was ist die model matrix an sich dann?
Ich versteh nicht ganz was du meinst.

Was ich machen will ist, einen Matrix Stack zu vermeiden.
Normalerweise hat man ja so einen Stack, packt da Transformationen drauf,
und die werden jeden Frame mit dem Model des Objekts, dem dieser Stack
zugewiesen wurde nacheinander multiplizeirt. (richtig?).

Ich will das aber so implementieren, das die model matrix quasi den aktuellen Zustand
beschreibt, der auch jederzeit verändert werden kann, ohne über vorherige Transformationen
bescheid zu wissen. Ich will also eine Reihe von operationen

rotateAboutGlobalX..Y..Z, rotateAboutObjectRelativeX..Y..Z, translateOnGlobalAxisTo(x, y, z), translateOnObjectRelativeAxisTo(x, y, z)

ausführen können, wobei die Informationen die ich habe nur die aktuellen zustände (rotation x, y, z und translation x, y, z) sind, und die model matrix an sich.

Ist das überhaupt möglich?

Also ich wende Rotationen, Scales und Translationen immer auf die Matrix des Objektes an, Scales natürlich nur einmal. Dann geh ich den SzeneGraph wie folgt durch: (der Szenegraph selbst ist dabei der Rootknoten eines Objekt3D, Object3D ist Iterable und kann selbst weitere Object3D aufnehmen)

  pushMatrix();
  multMatrix(obj.getMatrix());
  obj.render();
  for(Object3D o : obj) {
    drawObject(o);
  }
  popMatrix();
}```Das ist das Standard-Stack-Prinzip. Die Objekte bewegt man dann absolut, also ohne, dass man die Rotationswinkel und Translationen noch mal irgendwo speichert bzw berechnet.

Ja, aber ich wollte dieses Stack Prinzip eben vermeiden.

Ich hab jetzt mal daran gearbeitet… prinzipiell ist es ja nicht schwer.

wil ich entlang der globalen axen bewegen, muss ich die rotation zurücksetzen (die kenne ich ja)
bewegen, und anschließend zurückrotieren. Will ich objektachsenbezogen bewegen, lasse ich diesen
schritt weg.
Das Problem ist jetzt nur… wenn ich nun entlang dieser objektachsen bewegt hab, wie bekomme ich dann
die Position im globalen koordinatensystem?.. Die brauche ich ja, um dann zum Beispiel wieder zu einem
bestimmten globalen punkt bewegen zu können…

*** Edit ***

interessant wäre auch so eine methode wie translateRelative(RenderObject o, Vector3f translation);
also das man etwas entlang der achsen eines anderen Objekts bewegt… aber dabei wäre das problem nur noch größer,
(und ich schaffs ja jetzt schon nicht) die globalen koordinaten zu bekommen…

Ich weis nicht, wieso du dich so gegen dieses Stack-Prinzip sträubst, zumal dann die Koordinaten, die Rotationen und die Scales als Absolutwerte in den Objektmatritzen der Top-Level-Objekte stehen (Deswegen verwendet man ja auch push- und popMatrix). Die Koordinaten sind dabei noch am einfachsten zu bekommen, schwieriger wird es bei den Scales und den Rotationen (Google mal nach SVD… Singular Value Decomposition, Englisch für Singulärwertzerlegung).

Nach deiner Art müsste jedes Objekt für sich seine Position, Scales und Rotationen in Vektoren (oder besser auch in einer Matrix) speichern, diese müssten dann mit Deltawerten (bzw. einer Deltamatrix) multipliziert werden und das Ergebnis dann wieder in die GLMatrix geschrieben werden. Dazu bräuchtest du also mehr als doppelt so viel Speicher und das Ganze ist auch um einiges umfangreicher und aufwändiger. MAW.: keine gute Idee.

ja das will ich doch sowieso, ob stackprinzip oder nicht, aber ich will doch jederzeit zB die Position eines Objektes abfragen können…?

Nein nicht mit delta werten multiplizieren… Wenn ich eine Aktion ausführe, zB eine translation, ja dann wird eine translation mit dem delta werde (newx - this.position.x) ausgeführt…

welche GLMatrix?

Gut, ich bin verwirrt.
Wie komm ich denn jetzt an die globalen positionen nach rotation und / oder bewegung?

Mit GLMatrix meine ich die Viewmatrix, also jene Matrix, an welcher das Objekt gerendert werden soll. Wie gesagt, kannst du die Position direkt aus der Objektmatrix lesen (WX, WY und WZ bzw. XW, YW und ZW, je nachdem ob Coloumn- oder RowOrdered). Die Scalewerte wären eigentlich XX, YY und ZZ, aber leider werden diese Werte auch von Rotationswerten überlagert. Deswegen müssen Scale- und Rotationswerte per SVD separiert werden, aber immerhin stehen die Werte die du haben willst stets in der Objektmatrix.

Ja aber die positition veraendert sich doch, wenn ich rotiere… aber nicht in der matrix?

Dann ist meine Matriximplementation wohl falsch… bei rotate rotiert das Objekt jedenfalls auf der Stelle:

	public Matrix rotate(double angle, double x, double y, double z) {
		angle = toRadians(angle);
		double sin = sin(angle);
		double cos = cos(angle);
		double nCos = (1.0 - cos);
		double d = sqrt(x * x + y * y + z * z);
		if (Double.isInfinite(x) || Double.isInfinite(y)
				|| Double.isInfinite(z)) {
			x = (!Double.isInfinite(x)) ? 0.0 : (x > 0.0) ? 1.0 : -1.0;
			y = (!Double.isInfinite(y)) ? 0.0 : (y > 0.0) ? 1.0 : -1.0;
			z = (!Double.isInfinite(z)) ? 0.0 : (z > 0.0) ? 1.0 : -1.0;
			d = sqrt(x * x + y * y + z * z);
		}
		if (d == 0.0 || Double.isNaN(d)) {
			x = y = z = 0.0;
		} else {
			x /= d;
			y /= d;
			z /= d;
		}
		double[] rot = new double[9];
		double[] scl = new double[3];
		getScaleRotate(rot, scl);
		rot[0] += (nCos * x * x + cos);
		rot[1] += (nCos * x * y - sin * z);
		rot[2] += (nCos * x * z + sin * y);
		rot[3] += (nCos * y * x + sin * z);
		rot[4] += (nCos * y * y + cos);
		rot[5] += (nCos * y * z - sin * x);
		rot[6] += (nCos * z * x - sin * y);
		rot[7] += (nCos * z * y + sin * x);
		rot[8] += (nCos * z * z + cos);
		xx = rot[0] * scl[0];
		xy = rot[1] * scl[1];
		xz = rot[2] * scl[2];
		yx = rot[3] * scl[0];
		yy = rot[4] * scl[1];
		yz = rot[5] * scl[2];
		zx = rot[6] * scl[0];
		zy = rot[7] * scl[1];
		zz = rot[8] * scl[2];
		return this;
	}```...liegt daran, dass die Positionswerte wx, wy und wz unangetastet bleiben. Will man hingegen, dass sich das Objekt um eine andere Achse als um ihr Zentrum dreht, muss man die Objektmatrix mit einer Drehmatrix multiplizieren.
MAW.: zur Zeit rotierst du die Objektmatrix anscheinend nicht, sondern multiplizierst sie mit einer Bewegungsmatrix, also einer Matrix mit Scale-, Rotate- und Translate-Elementen. Natürlich verändert das Objekt mit einer solchen dann auch seine Position.

ja… mein ich doch?.. :smiley:

hm… wenn ich um einen anderen punkt rotieren wollte (also andere achse) hab ich immer das objekt dahin bewegt,
dann dort auf der stelle rotiert, und dann wieder zurückbewegt… (auf der objektachse halt…)

[QUOTE=mymaksimus]wenn ich um einen anderen punkt rotieren wollte (also andere achse) hab ich immer das objekt dahin bewegt,
dann dort auf der stelle rotiert, und dann wieder zurückbewegt… (auf der objektachse halt…)[/QUOTE]Siehst du, wie Umständlich das ist? Das geht doch mit dem Stack-Prinzip viel schöner.
Z.B. bei einem Riesenrad mit 6 Gondeln… versuch mal so etwas ohne Stack-Prinzip hinzubekommen. Da drehst du nicht nur am Riesenrad, sondern überhaupt am Rad. :wink:

das wäre dann im pseudo code:

BigWheel#update(){
    rotate();
    for(Gondola g : gondolas){
        g.moveTo(this.centerx + sin(this.radius), this.centery + cos(this.radius), z);
    }
}

aber hast recht, irgendwie ist das umständlich…

ja dann schau ich mir mal an wie das mit dem stack funktionieren soll >.<

Hm ich versteh immer noch nicht so ganz, wie ich das umsetzen kann. (#brettvormkopf)
In Post #4 hast du ja dein beispiel gespostet…

mit pushMatrix() setzt du irgendeine matrix (welche denn?) als “aktuelle matrix”, also das “wo du gerade wie bist”
(wenn ich dem hier glauben schenken darf und deine erklärung verstanden habe).
dann, multiplizierst du diese aktuelle mit… der matrix des objekts, was gerade gezeichnet werden soll…?
Und pop matrix… macht dann wieder die vorherige matrix als aktuelle matrix… richtig?

Die positition des Objekts steht dann in der matrix des selben… das macht ja sinn.
(Aber dann wird diese matrix ja jeden frame verändert?)

Verändert wird nur die aktuelle View-Matrix. Deswegen verwendet man push und pop (Stack-Operatoren). Zunächst stellst du also die View-Matrix ein und dann rekursiv über die gesammte Szene (Baum aus Object3D)…

  1. pushMatrix()… sichert die aktuelle View-Matrix auf dem Stack
  2. View-Matrix mit Objekt-Matrix multiplizieren und in View-Matrix speichern
  3. Objekt rendern
  4. popMatrix()… zuvor gesicherte Matrix wieder in View-Matrix schreiben.

Nachdem der gesamte Szene-Baum abgearbeitet ist, befindet sich die View-Matrix wieder in dem Zustand, in welchem sie vor der Abarbeitung war. Sicher wird sie während der Abarbeitung des Öfteren verändert, aber egal… dafür sichert man sie ja immer. :wink: