Fix, damit sich die 3D-Kamera in die Richtung bewegt, in die sie zeigt?
Ich habe einCamera
an ein @ angehänSceneNode
und Bewegung funktioniert gut, solange dieSceneNode
's Rotation / Achsen sind mit der Welt ausgerichtet. Wenn sich ein Objekt dreht, um in eine andere Richtung zu "schauen", und aufgefordert wird, sich "vorwärts" zu bewegen, geschieht dies jedochnich Bewegen Sie sich in die neue Richtung "Vorwärts". Stattdessen bewegt es sich weiter in die gleiche Richtung, in die es gewandt warVo Die Drehung wurde angewendet.
Ich habe ein Szenendiagramm zum Verwalten einer 3D-Szene. Die Grafik ist ein Baum vonSceneNode
Objekte, die über ihre Transformationen in Bezug auf ihre Eltern und die Welt Bescheid wissen.
Wie in der TL; DR; Snippet, stell dir vor, du hast eincameraNode
mit Nullrotation (z. B. nach Norden) und drehen Sie dann dascameraNode
90 Grad nach links um die + Y "Auf" -Achse, d. H. Nach Westen schauen lassen. Soweit ist alles in Ordnung. Wenn Sie jetzt versuchen, das @ zu verschiebcameraNode
"Vorwärts", das ist jetzt im Westen, diecameraNode
bewegt sich stattdessen so, als ob "vorwärts" immer noch nach Norden zeigt.
urz gesagt, es bewegt sich als ob es noch nie gedreht worden wäre.
Der folgende Code zeigt, was ich zuletzt versucht habe und meine (derzeitige) Vermutung, die Bereiche einzugrenzen, die am wahrscheinlichsten mit dem Problem zusammenhängen.
RelevantSceneNode
MitgliederDasSceneNode
ie @ -Implementierung enthält die folgenden Felder (nur die für diese Frage relevanten Felder werden angezeigt):
class GenericSceneNode implements SceneNode {
// this node's parent; always null for the root scene node in the graph
private SceneNode parentNode;
// transforms are relative to a parent scene node, if any
private Vector3 relativePosition = Vector3f.createZeroVector();
private Matrix3 relativeRotation = Matrix3f.createIdentityMatrix();
private Vector3 relativeScale = Vector3f.createFrom(1f, 1f, 1f);
// transforms are derived by combining transforms from all parents;
// these are relative to the world --in world space
private Vector3 derivedPosition = Vector3f.createZeroVector();
private Matrix3 derivedRotation = Matrix3f.createIdentityMatrix();
private Vector3 derivedScale = Vector3f.createFrom(1f, 1f, 1f);
// ...
}
Adding aCamera
zu einer Szene bedeutet einfach, dass sie an ein @ angehängt wiSceneNode
in der Grafik. Seit derCamera
hat keine eigenen Positions- / Rotationsinformationen, der Client handhabt einfach dasSceneNode
zu dem dasCamera
ist beigefügt und das war's.
Außer dem in dieser Frage erwähnten Problem scheint alles andere wie erwartet zu funktionieren.
SceneNode
ÜbersetzunDie Mathematik zur Übersetzung des Knotens in eine bestimmte Richtung ist unkompliziert und lautet im Grunde genommen:
currentPosition = currentPosition + normalizedDirectionVector * offset;
DasSceneNode
Implementierung folgt:
@Override
public void moveForward(float offset) {
translate(getDerivedForwardAxis().mult(-offset));
}
@Override
public void moveBackward(float offset) {
translate(getDerivedForwardAxis().mult(offset));
}
@Override
public void moveLeft(float offset) {
translate(getDerivedRightAxis().mult(-offset));
}
@Override
public void moveRight(float offset) {
translate(getDerivedRightAxis().mult(offset));
}
@Override
public void moveUp(float offset) {
translate(getDerivedUpAxis().mult(offset));
}
@Override
public void moveDown(float offset) {
translate(getDerivedUpAxis().mult(-offset));
}
@Override
public void translate(Vector3 tv) {
relativePosition = relativePosition.add(tv);
isOutOfDate = true;
}
Ander als das in dieser Frage erwähnte Problem, Dinge herum wie erwartet.
SceneNode
RotationDie Client-Anwendung dreht dascameraNode
wie folgt
final Angle rotationAngle = new Degreef(-90f);
// ...
cameraNode.yaw(rotationAngle);
Und dieSceneNode
ie Implementierung von @ ist auch ziemlich einfach:
@Override
public void yaw(Angle angle) {
// FIXME?: rotate(angle, getDerivedUpAxis()) accumulates other rotations
rotate(angle, Vector3f.createUnitVectorY());
}
@Override
public void rotate(Angle angle, Vector3 axis) {
relativeRotation = relativeRotation.rotate(angle, axis);
isOutOfDate = true;
}
Der Mathe- / Code für die Drehung ist in einem 3x3-Matrixobjekt eingekapselt. Beachten Sie, dass Sie während der Tests sehen können, wie die Szene um die Kamera gedreht wird, sodass tatsächlich Drehungen angewendet werden, was dieses Problem für mich noch rätselhafter macht.
Direction VectorsDie Richtungsvektoren sind einfach Spalten aus der abgeleiteten 3x3-Rotationsmatrix relativ zur Welt:
@Override
public Vector3 getDerivedRightAxis() {
return derivedRotation.column(0);
}
@Override
public Vector3 getDerivedUpAxis() {
return derivedRotation.column(1);
}
@Override
public Vector3 getDerivedForwardAxis() {
return derivedRotation.column(2);
}
Computing Derived TransformsWenn es relevant ist, ist dies, wie dieparentNode
-Transformationen werden kombiniert, um die abgeleiteten Transformationen von @ zu berechnethis
instance:
private void updateDerivedTransforms() {
if (parentNode != null) {
/**
* derivedRotation = parent.derivedRotation * relativeRotation
* derivedScale = parent.derivedScale * relativeScale
* derivedPosition = parent.derivedPosition + parent.derivedRotation * (parent.derivedScale * relativePosition)
*/
derivedRotation = parentNode.getDerivedRotation().mult(relativeRotation);
derivedScale = parentNode.getDerivedScale().mult(relativeScale);
Vector3 scaledPosition = parentNode.getDerivedScale().mult(relativePosition);
derivedPosition = parentNode.getDerivedPosition().add(parentNode.getDerivedRotation().mult(scaledPosition));
} else {
derivedPosition = relativePosition;
derivedRotation = relativeRotation;
derivedScale = relativeScale;
}
Matrix4 t, r, s;
t = Matrix4f.createTranslationFrom(relativePosition);
r = Matrix4f.createFrom(relativeRotation);
s = Matrix4f.createScalingFrom(relativeScale);
relativeTransform = t.mult(r).mult(s);
t = Matrix4f.createTranslationFrom(derivedPosition);
r = Matrix4f.createFrom(derivedRotation);
s = Matrix4f.createScalingFrom(derivedScale);
derivedTransform = t.mult(r).mult(s);
}
Dies wird verwendet, um Transformationen durch das Szenendiagramm zu verbreiten, so dass untergeordneteSceneNode
s können die Transformationen ihrer Eltern berücksichtigen.
Ich habe in den letzten ~ 3 Wochen vor dem Posten dieser Frage mehrere Antworten innerhalb und außerhalb von SO durchgesehen (z. B.Hie, Hie, Hie, undHie, unter anderem). Offensichtlich waren sie, obwohl verwandt, in meinem Fall wirklich nicht hilfreich.
Antworten auf Fragen in den KommentarenSind Sie sicher, dass beim RechnenderivedTransform
deine ElternderivedTransform
wird bereits berechnet?
Ja, das übergeordneteSceneNode
wird immer aktualisiert, bevor untergeordnete Elemente aktualisiert werden. Dasupdate
Logik ist:
@Override
public void update(boolean updateChildren, boolean parentHasChanged) {
boolean updateRequired = parentHasChanged || isOutOfDate;
// update this node's transforms before updating children
if (updateRequired)
updateFromParent();
if (updateChildren)
for (Node n : childNodesMap.values())
n.update(updateChildren, updateRequired);
emitNodeUpdated(this);
}
@Override
public void updateFromParent() {
updateDerivedTransforms(); // implementation above
isOutOfDate = false;
}
Dieses Stück ruft die private Methode im vorherigen Abschnitt auf.