Исправлена ​​​​ошибка, из-за которой 3D-камера двигалась в том направлении, в котором она смотрит?

Краткая версия (TL; DR)

У меня есть Camera, прикрепленный к SceneNode, и движение работает нормально, пока вращение/оси SceneNode выровнены с миром. Однако, когда объект поворачивается, чтобы «смотреть» в другом направлении, и получает указание двигаться «вперед», он не движется в новом направлении «вперед». Вместо этого он продолжает двигаться в том же направлении, в котором был до поворота.

Детали и пример

У меня есть график сцены для управления 3D-сценой. Граф представляет собой дерево из SceneNode объектов, которые знают о своих преобразованиях относительно своего родителя и мира.

Согласно TL;DR; фрагмент, представьте, что у вас есть cameraNode с нулевым поворотом (например, лицом на север), а затем поверните cameraNode на 90 градусов влево вокруг оси +Y «вверх», то есть сделайте так, чтобы он смотрел на запад. Пока все в порядке. Если вы теперь попытаетесь переместить 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 могли учитывать преобразования своих родителей.


Другие/связанные вопросы

Я просмотрел несколько ответов внутри и вне SO за последние ~ 3 недели до публикации этого вопроса (например, здесь, здесь, здесь и здесь среди нескольких других). Очевидно, хотя и связанные, они действительно не помогли в моем случае.


Ответы на вопросы в комментариях

Вы уверены, что при вычислении 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;
}

Эта часть вызывает закрытый метод из предыдущего раздела.


person code_dredd    schedule 08.08.2016    source источник
comment
Какую версию OpenGL вы используете?   -  person Francis Cugler    schedule 08.08.2016
comment
@FrancisCugler Я использую OpenGL 4.5. Вам нужно, чтобы я выложил шейдеры?   -  person code_dredd    schedule 08.08.2016
comment
Нет; просто хотел узнать, используете ли вы современный OpenGL или старые вызовы API 1.0. Я не знаком с Java, но я работал с OpenGL на С++. Я знаю, что в OpenGL нет объекта камеры, и вам нужно создать его и прикрепить к сцене. Затем, как только вы визуализируете его, его перемещение и вращение зависят от Handedness вашей определенной системы координат. Ваши матричные расчеты MVP (Model View Projection) важны; и порядок, в котором вы делаете их для вращения, преобразования и масштабирования, также важен.   -  person Francis Cugler    schedule 08.08.2016
comment
@FrancisCugler Я понимаю. Объекты Camera и SceneNode были определены мной. Даже если вы не знакомы с Java, вы знакомы с C++ и OpenGL, поэтому вы, вероятно, уже понимаете семантику приведенного выше кода (без перегрузки операторов). Возможно, вы все еще сможете заметить что-то, что я пропустил (например, математику или что-то еще). Если вы хотите, чтобы я добавил больше информации, дайте мне знать, что это будет.   -  person code_dredd    schedule 08.08.2016
comment
Вы пытались просто визуализировать камеру отдельно вместо того, чтобы вкладывать ее узел в узел сцены?   -  person Francis Cugler    schedule 08.08.2016
comment
@FrancisCugler Да, у меня есть отдельная ветка разработки в моем репозитории, где Camera имеет свою собственную информацию, и ему не нужно полагаться на SceneNode для своей позиции / поворота, и он может двигаться самостоятельно. Он ведет себя точно так же, как и выше, с той же проблемой.   -  person code_dredd    schedule 08.08.2016
comment
Давайте продолжим обсуждение в чате.   -  person Francis Cugler    schedule 08.08.2016
comment
Вы уверены, что при вычислении derivedTransform производное преобразование вашего родителя уже вычислено? Или, может быть, вы могли бы создать сцену с одним узлом и подключенной камерой и отладить свои вычисления?   -  person kolenda    schedule 11.08.2016
comment
@kolenda, да, я уверен. SceneNode обновит себя, прежде чем пытаться обновить любые дочерние элементы, которые у него могут быть.   -  person code_dredd    schedule 12.08.2016
comment
@kolenda, Кроме того, у меня уже есть 2 демонстрационные программы с узлами сцены и камерами, показывающими проблему. Не могли бы вы пояснить свой 2-й вопрос?   -  person code_dredd    schedule 12.08.2016
comment
Я хотел создать только один узел, прикрепить к нему камеру и вращать узел. Затем выйдите из системы или отладьте значения для локального/глобального вращения и прямого вектора. Вы должны получить некоторые неправильные значения, но в такой простой настройке будет легче обнаружить ошибку.   -  person kolenda    schedule 12.08.2016
comment
@kolenda Я сделал это за несколько дней до того, как опубликовал этот вопрос. Ничто не выделялось для меня как ага!, вот в чем проблема. Я стараюсь, насколько это возможно, не публиковать вопросы... Обычно к тому времени, когда я публикую сообщения, у меня заканчиваются идеи (что составляет 1,5 с лишним недели предыдущих попыток отладки).   -  person code_dredd    schedule 15.08.2016


Ответы (1)


Это не прямой ответ, а ссылка на запрос ОП.

OpenGL v1.0 с использованием старых вызовов API: реализация объекта класса камеры при использовании его в классе сцены вне графа сцены класса сцены. Это написано на С++

Камера.h

#ifndef CAMERA_H
#define CAMERA_H

#include "Core.h"

class Camera {    
private:
    Vector3 _v3EyePosition;
    Vector3 _v3LookCenter;
    Vector3 _v3Up;

public:
    Camera();
    ~Camera();    

    void Get3rdPersonLocation( Vector3 &v3Position, float &fAngle );
    void Set( Vector3 v3EyePosition, Vector3 v3LookCenter, Vector3 v3Up = Vector3( 0.0f, 1.0f, 0.0f ) );
    void Render();    
}; 

#endif

Камера.cpp

#include "stdafx.h"
#include "Camera.h"

Camera::Camera() {    
    _v3EyePosition = Vector3( 0.0f, 0.0f,  0.0f );
    _v3LookCenter  = Vector3( 0.0f, 0.0f, -1.0f );
    _v3Up          = Vector3( 0.0f, 1.0f,  0.0f );    
} 

Camera::~Camera() {
} 

void Camera::Get3rdPersonLocation( Vector3 &v3Position, float &fAngle ) {   
    v3Position._fX = _v3LookCenter._fX;
    v3Position._fY = _v3EyePosition._fY;
    v3Position._fZ = _v3LookCenter._fZ;

    // Find Angle
    float fX = _v3LookCenter._fX - _v3EyePosition._fX;
    float fZ = _v3LookCenter._fZ - _v3EyePosition._fZ;

    // Angle In Degrees
    fAngle = Math::Radian2Degree( atan2( fX, fZ ) );    
}     

void Camera::Set( Vector3 v3EyePosition, Vector3 v3LookCenter, Vector3 v3Up ) {    
    _v3EyePosition = v3EyePosition;
    _v3LookCenter  = v3LookCenter;
    _v3Up          = v3Up;    
}

void Camera::Render() {     
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    gluLookAt( _v3EyePosition._fX, _v3EyePosition._fY, _v3EyePosition._fZ,
               _v3LookCenter._fX,  _v3LookCenter._fY,  _v3LookCenter._fZ,
               _v3Up._fX,          _v3Up._fY,          _v3Up._fZ );     
}

В функции Render Camera, использующей старые вызовы API OpenGL, мы сначала загружаем матрицу Modelview, затем загружаем матрицу идентичности; затем мы, наконец, используем метод glu gluLookAt(...) для установки позиций необходимых векторов.

Scene.h — имеет много членов и функций; но что касается объекта Camera, он имеет камеру в качестве члена, а не указатель на камеру.

Scene.cpp — Render()

void Scene::Render() {    
    // Update Camera
    _Camera.Set( _Player.GetPosition(), _Player.GetLookCenter() );

    // Position Camera
    _Camera.Render();    

    if ( UserSettings::Get()->_bQuit ) {
        return;
    }

    if ( _vpNodes.size() < 1 ) {
        // No SceneGraph To Render
        return;
    }

    EnableLights();

    // Send Items To Be Rendered
    // Clear 2nd Render Pass Container
    DeleteAllAlphaObjects();

    // Render All Opaque Objects (1st Pass) & Store 2nd Pass Objects
    _vpNodes[0]->RenderOGL( false, true );

    // Render All Objects With Alpha Values (2nd Pass)
    glEnable( GL_BLEND );
    glMatrixMode( GL_MODELVIEW );

    for ( std::vector<AlphaObject*>::iterator it = _vpAlphaObjects.begin(); it != _vpAlphaObjects.end(); ++it ) {
        // Set Model View Matrix
        glMatrixMode( GL_MODELVIEW );
        glPushMatrix();
        glLoadMatrixf( &(*it)->f16Matrix[0] );

        (*it)->pShape->RenderOGL( true, false );

        glMatrixMode( GL_MODELVIEW );
        glPopMatrix();
    }

    // Show Selected Weapon
    _Player.RenderWeapon();

    glDisable( GL_BLEND );

    DisableLights();

    return;    
} 

Здесь Camera не зависит от класса Player, а также от иерархии графа сцены сцены, и мы используем Camera в вызове сцены Render. Здесь мы устанавливаем Camera, получая текущую позицию Player и направление Player's LookCenter.

РЕДАКТИРОВАНИЕ: добавление класса игрока и связанного с ним кода для расчета перемещений

enum Action {
    NO_ACTION = -1,
    MOVING_FORWARD = 0,
    MOVING_BACK,
    MOVING_LEFT,
    MOVING_RIGHT,
    LOOKING_LEFT,
    LOOKING_RIGHT,
    LOOKING_UP,
    LOOKING_DOWN,
}; // Action

Player.h

#ifndef PLAYER_H
#define PLAYER_H

#include "Core.h"

class Weapon;
class NodeTransform;

class Player {
private:
    enum MouseLook {
        ML_NORMAL = 1,
        ML_INVERT = -1,
    } _MouseLookState; // MouseLook

    Vector3 _v3Position;
    Vector3 _v3LookCenter;

    float _fLookDistance;
    float _fMaxUp;
    float _fMaxDown;

    float _fLinearSpeed;
    float _fAngularSpeed;

public:
    Player( float fLookDistance );
    ~Player();

    void    SetSpeed( float fLinear, float fAngular );

    void    SetMouseY( bool bInvert );
    void    SetLocation( Vector3 v3Position, Vector3 v3Direction = Vector3( 0.0f, 0.0f, -1.0f ) );
    void    Move( Action action, float fDeltaTime );

    bool    Update();   

    inline void     SetPosition( Vector3 v3Position );
    inline Vector3  GetPosition();
    inline Vector3  GetLookCenter();
    inline Vector3  GetLookDirection();         
};

inline void Player::SetPosition( Vector3 v3Position ) {
    Vector3 v3LookDirection;
    v3LookDirection = _v3LookCenter - _v3Position;

    _v3Position   = v3Position;
    _v3LookCenter = v3Position + v3LookDirection;
}

inline Vector3 Player::GetPosition() {  
    return _v3Position;
} 

inline Vector3 Player::GetLookCenter() {
    return _v3LookCenter;
} 

inline Vector3 Player::GetLookDirection() {    
    Vector3 v3LookDirection;
    v3LookDirection = _v3LookCenter - _v3Position;    
    v3LookDirection.Normalize();    
    return v3LookDirection;    
}

#endif

Player.cpp

#include "stdafx.h"
#include "Player.h"
#include "UserSettings.h"
#include "NodeTransform.h"

Player::Player( float fLookDistance ) {    
    _fLookDistance  = fLookDistance;    
    // Calculate Maximum Limits For Looking Up And Down
    _fMaxUp         = _fLookDistance * tan( Math::Degree2Radian( 50 ) );
    _fMaxDown       = _fLookDistance * tan( Math::Degree2Radian( 40 ) );

    _v3Position     = Vector3( 0.0f, 0.5f, 0.0f );
    _v3LookCenter   = Vector3( 0.0f, 0.5f, -fLookDistance );

    _fLinearSpeed   = 15.0f; // Units Per Second
    _fAngularSpeed  = 3.0f; // Radians Per Second

    SetMouseY( UserSettings::Get()->GetMouseInvert() );    
} 

Player::~Player() {
} // ~Player

void Player::SetMouseY( bool bInvert ) {    
    if ( bInvert ) {
        _MouseLookState = ML_INVERT;
    } else {
        _MouseLookState = ML_NORMAL;
    }       
} 

void Player::SetLocation( Vector3 v3Position, Vector3 v3Direction ) {    
    _v3Position   = v3Position;
    _v3LookCenter = v3Position + _fLookDistance*v3Direction;    
}

void Player::Move( Action action, float fDeltaTime ) {    
    Vector3 v3LookDirection;
    v3LookDirection = _v3LookCenter - _v3Position;

    switch ( action ) {
        case MOVING_FORWARD: {
            // Prevent Vertical Motion
            v3LookDirection._fY = 0.0f;
            _v3Position   += v3LookDirection*fDeltaTime*_fLinearSpeed;
            _v3LookCenter += v3LookDirection*fDeltaTime*_fLinearSpeed;
            break;
        }
        case MOVING_BACK: {
            // Prevent Vertical Motion
            v3LookDirection._fY = 0.0f;
            _v3Position   -= v3LookDirection*fDeltaTime*_fLinearSpeed;
            _v3LookCenter -= v3LookDirection*fDeltaTime*_fLinearSpeed;
            break;
        }
        case MOVING_LEFT: {
            // Get "Side" Direction & Prevent Vertical Motion
            v3LookDirection._fY = v3LookDirection._fX;
            v3LookDirection._fX = -v3LookDirection._fZ;
            v3LookDirection._fZ = v3LookDirection._fY;
            v3LookDirection._fY = 0.0f;

            _v3Position   -= v3LookDirection*fDeltaTime*_fLinearSpeed;
            _v3LookCenter -= v3LookDirection*fDeltaTime*_fLinearSpeed;
            break;
        }
        case MOVING_RIGHT: {
            // Get "Side" Direction & Prevent Vertical Motion
            v3LookDirection._fY = v3LookDirection._fX;
            v3LookDirection._fX = -v3LookDirection._fZ;
            v3LookDirection._fZ = v3LookDirection._fY;
            v3LookDirection._fY = 0.0f;

            _v3Position   += v3LookDirection*fDeltaTime*_fLinearSpeed;
            _v3LookCenter += v3LookDirection*fDeltaTime*_fLinearSpeed;
            break;
        }
        case LOOKING_LEFT: {

            /*float fSin = -sin( fDeltaTime*_fAngularSpeed );
            float fCos =  cos( fDeltaTime*_fAngularSpeed );

            _v3LookCenter._fX = _v3Position._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
            _v3LookCenter._fZ = _v3Position._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
            break;*/

            // Third Person
            float fSin = sin( fDeltaTime*_fAngularSpeed );
            float fCos = -cos( fDeltaTime*_fAngularSpeed );

            _v3Position._fX = _v3LookCenter._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
            _v3Position._fZ = _v3LookCenter._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
            break;
        }
        case LOOKING_RIGHT: {
            /*float fSin = sin( fDeltaTime*_fAngularSpeed );
            float fCos = cos( fDeltaTime*_fAngularSpeed );

            _v3LookCenter._fX = _v3Position._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
            _v3LookCenter._fZ = _v3Position._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
            break;*/

            // Third Person
            float fSin = -sin( fDeltaTime*_fAngularSpeed );
            float fCos = -cos( fDeltaTime*_fAngularSpeed );

            _v3Position._fX = _v3LookCenter._fX + (-fSin * v3LookDirection._fZ + fCos * v3LookDirection._fX );
            _v3Position._fZ = _v3LookCenter._fZ + ( fCos * v3LookDirection._fZ + fSin * v3LookDirection._fX );
            break;
        }
        case LOOKING_UP: {
            _v3LookCenter._fY -= fDeltaTime*_fAngularSpeed*_MouseLookState;

            // Check Maximum Values
            if ( _v3LookCenter._fY > (_v3Position._fY + _fMaxUp ) ) {
                _v3LookCenter._fY = _v3Position._fY + _fMaxUp;
            } else if ( _v3LookCenter._fY < (_v3Position._fY - _fMaxDown) ) {
                _v3LookCenter._fY = _v3Position._fY - _fMaxDown;
            }
            break;
        }
    }    
}

bool Player::Update() {     
    // Stripped Down This Deals With Player's Weapons    
} 

void Player::SetSpeed( float fLinear, float fAngular ) {        
    _fLinearSpeed  = fLinear;
    _fAngularSpeed = fAngular;    
} 

Scene.h — то же, что и для камеры; есть объект игрока, а не указатель на объект игрока. Однако есть указатель на playerTransform, который является NodeTransform. Здесь слишком много функций, чтобы перечислять их из-за взаимодействия игрока со сценой, поскольку это работающая 3D-игра. Я могу предоставить несколько функций, которые могут представлять интерес.

Scene.cpp Scene::Update()

// -----------------------------------------------------------------------
// Update
// Animate Objects, Pickup Checks Etc. This All Happens At The
// Physics Refresh Rate
void Scene::Update() {

    UserSettings* pUserSettings = UserSettings::Get();
    AudioManager* pAudio = AudioManager::GetAudio();

    bool bPlayerMoving = false;

    // Movement
    if ( pUserSettings->IsAction( MOVING_FORWARD ) ) {
        _Player.Move( MOVING_FORWARD, GameOGL::GetPhysicsTimeStep() );
        bPlayerMoving = true;
    }

    if ( pUserSettings->IsAction( MOVING_BACK ) ) {
        _Player.Move( MOVING_BACK, GameOGL::GetPhysicsTimeStep() );
        bPlayerMoving = true;
    }

    if ( pUserSettings->IsAction( MOVING_LEFT ) ) {
        _Player.Move( MOVING_LEFT, GameOGL::GetPhysicsTimeStep() );
        bPlayerMoving = true;
    }

    if ( pUserSettings->IsAction( MOVING_RIGHT ) ) {
        _Player.Move( MOVING_RIGHT, GameOGL::GetPhysicsTimeStep() );
        bPlayerMoving = true;
    }    

    if ( bPlayerMoving && !_bPlayerWalking ) {
        pAudio->SetLooping( AUDIO_FOOTSTEPS, true );
        pAudio->Play( AUDIO_FOOTSTEPS );
        _bPlayerWalking = true;
    }
    else if ( !bPlayerMoving && _bPlayerWalking ) {
        pAudio->Stop( AUDIO_FOOTSTEPS );
        _bPlayerWalking = false;
    }  

    // ... Other Code Here    
}

EDIT — Добавление NodeTransform::Render() — Показать порядок операций для MVP

// Move Model View Matrix M = (T C R S C^)
void NodeTransform::RenderOGL( bool bSecondPass, bool bRenderNext ) {    
    if ( _pIn && _bVisible ) {
        // Put Matrix Onto Stack For Later Retrieval
        glMatrixMode( GL_MODELVIEW );
        glPushMatrix();

        if ( _bHaveMatrix ) {
            // Use Transformation Matrix
            glMultMatrixf( &_f16Matrix[0] );
        }

        // Transalate
        glTranslatef( _v3Translate._fX, _v3Translate._fY, _v3Translate._fZ );

        // Move Back To Center
        glTranslatef( _v3Center._fX, _v3Center._fY, _v3Center._fZ );

        // Rotate
        glRotatef( _fRotateAngle, _v3RotateAxis._fX, _v3RotateAxis._fY, _v3RotateAxis._fZ );

        // Scale
        glScalef( _v3Scale._fX, _v3Scale._fY, _v3Scale._fZ );

        // Offset By -ve Center Value
        glTranslatef( -_v3Center._fX, -_v3Center._fY, -_v3Center._fZ );

        // Move Down The Tree
        _pIn->RenderOGL( bSecondPass, true );

        // Get Old Matrix
        glMatrixMode( GL_MODELVIEW );
        glPopMatrix();
    }

    if ( _pNext && bRenderNext ) {
        _pNext->RenderOGL( bSecondPass, true );
    }    
} // RenderOGL
person Francis Cugler    schedule 08.08.2016
comment
Однако ваши вычисления для перемещения Player в направлении, в котором он смотрит, кажутся похожими на мои. - person code_dredd; 08.08.2016
comment
Просмотрел довольно большой дамп кода более подробно, но, к сожалению, он не слишком помог мне понять, что я делаю неправильно. Математика похожа на мою, особенно движение игрока, но кроме этого, в посте много кода, не слишком относящегося к основной проблеме, с которой я столкнулся. Тем не менее, спасибо за попытку. - person code_dredd; 09.08.2016
comment
Совершенно никаких проблем; Я не против помочь другим, где и когда я могу. Мне нравится помогать, чтобы однажды, когда мне может понадобиться помощь, другие сделали бы то же самое. - person Francis Cugler; 09.08.2016