Исправлена ошибка, из-за которой 3D-камера двигалась в направлении, в котором она стоит?
у меня естьCamera
прикреплен кSceneNode
и движение работает нормально, покаSceneNode
Вращение / оси ориентированы на мир. Однако, когда объект вращается, чтобы «смотреть» в другом направлении, и ему приказывают двигаться «вперед», это делаетне двигаться по новому «прямому» направлению. Вместо этого он продолжает двигаться в том же направлении, что и переддо вращение было применено.
У меня есть график сцены для управления 3D-сценой. Граф это деревоSceneNode
объекты, которые знают о своих трансформациях относительно своего родителя и мира.
Согласно TL; DR; фрагмент, представьте, что у вас естьcameraNode
с нулевым вращением (например, лицом к северу), а затем повернитеcameraNode
На 90 градусов влево вокруг оси + «вверх», то есть на запад. Пока все хорошо. Если вы сейчас попытаетесь переместитьcameraNode
«вперед», который сейчас на западе,cameraNode
вместо этого двигается, как будто «вперед» все еще смотрят на север.
Короче, двигаетсякак будто он никогда не вращался в первую очередь.
Приведенный ниже код показывает, что я пытался сделать совсем недавно, и мое (текущее) лучшее предположение о сужении областей, наиболее вероятно связанных с проблемой.
СоответствующийSceneNode
членыSceneNode
реализация имеет следующие поля (показаны только те, которые имеют отношение к этому вопросу):
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);
// ...
}
ДобавлениеCamera
к сцене просто означает, что она привязана кSceneNode
на графике. ПосколькуCamera
не имеет собственной позиционной / вращательной информации, клиент просто обрабатываетSceneNode
к которомуCamera
прилагается и все.
За исключением проблемы, упомянутой в этом вопросе, все остальное работает как положено.
SceneNode
ПереводМатематика для перевода узла в определенном направлении проста и в основном сводится к:
currentPosition = currentPosition + normalizedDirectionVector * offset;
SceneNode
реализация следует:
@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;
}
Кроме проблемы, упомянутой в этом вопросе, все вокруг, как и ожидалось.
SceneNode
вращениеКлиентское приложение вращаетcameraNode
следующее:
final Angle rotationAngle = new Degreef(-90f);
// ...
cameraNode.yaw(rotationAngle);
ИSceneNode
реализация также довольно проста:
@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;
}
Математический / код для вращения заключен в матричном объекте 3x3. Обратите внимание, что во время тестов вы можете видеть, что сцена вращается вокруг камеры, поэтому вращение действительно применяется, что делает эту проблему еще более загадочной для меня.
Векторы направленияВекторы направления - это просто столбцы, взятые из полученной матрицы вращения 3x3 относительно мира:
@Override
public Vector3 getDerivedRightAxis() {
return derivedRotation.column(0);
}
@Override
public Vector3 getDerivedUpAxis() {
return derivedRotation.column(1);
}
@Override
public Vector3 getDerivedForwardAxis() {
return derivedRotation.column(2);
}
Вычисление производных преобразованийЕсли это актуально, то вот какparentNode
преобразования объединяются для вычисления производных преобразованийthis
пример:
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);
}
Это используется для распространения преобразований через граф сцены, так что дочернийSceneNode
s может принять во внимание преобразования своих родителей.
За последние ~ 3 недели до публикации этого вопроса я прошел через несколько ответов внутри и за пределами SO (например,Вот, Вот, Вот, а такжеВотсреди нескольких других). Очевидно, что, хотя и связаны, они действительно не помогли в моем случае.
Ответы на вопросы в комментарияхВы уверены, что при вычисленииderivedTransform
ваши родителиderivedTransform
уже вычислено?
Да, родительSceneNode
всегда обновляется перед обновлением детей.update
логика это:
@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;
}
Этот кусок вызывает приватный метод в предыдущем разделе.