Fix for 3D camera to move in the direction it's facing?

前端 未结 1 1147
灰色年华
灰色年华 2020-12-18 22:47

The Short Version (TL;DR)

I have a Camera attached to a SceneNode and movement works fine as long as the SceneNode\'s rotati

相关标签:
1条回答
  • 2020-12-18 23:40

    This is not meant to be a direct answer but as reference upon the request of the OP.

    OpenGL v1.0 using old API calls: Implementation of a Camera Class Object while using it in a Scene Class outside of the Scene Class's Scene Graph. This is written in C++

    Camera.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
    

    Camera.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 );     
    }
    

    In the Camera's Render function using the old OpenGL API calls we first load in the Modelview matrix, then we load the identity matrix; then we finally use glu's gluLookAt(...) method to set the positions of the needed vectors.

    Scene.h - Has many members and functions; but as in regards with the Camera Object it has a Camera as a member and not a pointer to a 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;    
    } 
    

    Here the Camera is independent of the Player class as well as the Scene's Scene Graph Hierarchy and we use the Camera in the Scene's Render Call. Here we set the Camera by getting the Player's current Position, and the Player's LookCenter direction.

    EDIT - Adding Player Class And Related Code For Movement Calculations

    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 - Same here as for the Camera; there is a Player Object and not a pointer to a player object. However there is a pointer to a playerTransform which is a NodeTransform. There are too many functions to list here because of the interaction of the Player with the Scene since this is a working 3D Game. I can provide a few functions that may be of interest.

    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 - Adding NodeTransform::Render() - Show the order of operations for 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
    
    0 讨论(0)
提交回复
热议问题