Browse Source

Merge pull request #325 from DavidWyand-GG/GameObjectBanking

Game cam and eye banking, control schemes
David Wyand 12 years ago
parent
commit
2123365d4d

+ 237 - 68
Engine/source/T3D/camera.cpp

@@ -35,6 +35,12 @@
 #include "math/mathUtils.h"
 #include "math/mathUtils.h"
 #include "math/mTransform.h"
 #include "math/mTransform.h"
 
 
+#ifdef TORQUE_EXTENDED_MOVE
+   #include "T3D/gameBase/extended/extendedMove.h"
+#endif
+
+S32 Camera::smExtendedMovePosRotIndex = 0;  // The ExtendedMove position/rotation index used for camera movements
+
 #define MaxPitch 1.5706f
 #define MaxPitch 1.5706f
 #define CameraRadius 0.05f;
 #define CameraRadius 0.05f;
 
 
@@ -254,6 +260,7 @@ Camera::Camera()
 {
 {
    mNetFlags.clear(Ghostable);
    mNetFlags.clear(Ghostable);
    mTypeMask |= CameraObjectType;
    mTypeMask |= CameraObjectType;
+   mDataBlock = 0;
    mDelta.pos = Point3F(0.0f, 0.0f, 100.0f);
    mDelta.pos = Point3F(0.0f, 0.0f, 100.0f);
    mDelta.rot = Point3F(0.0f, 0.0f, 0.0f);
    mDelta.rot = Point3F(0.0f, 0.0f, 0.0f);
    mDelta.posVec = mDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f);
    mDelta.posVec = mDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f);
@@ -270,6 +277,9 @@ Camera::Camera()
    mObservingClientObject = false;
    mObservingClientObject = false;
    mMode = FlyMode;
    mMode = FlyMode;
 
 
+   mLastAbsoluteYaw = 0.0f;
+   mLastAbsolutePitch = 0.0f;
+
    // For NewtonFlyMode
    // For NewtonFlyMode
    mNewtonRotation = false;
    mNewtonRotation = false;
    mAngularVelocity.set(0.0f, 0.0f, 0.0f);
    mAngularVelocity.set(0.0f, 0.0f, 0.0f);
@@ -301,7 +311,7 @@ Camera::~Camera()
 
 
 bool Camera::onAdd()
 bool Camera::onAdd()
 {
 {
-   if(!Parent::onAdd())
+   if(!Parent::onAdd() || !mDataBlock)
       return false;
       return false;
 
 
    mObjBox.maxExtents = mObjScale;
    mObjBox.maxExtents = mObjScale;
@@ -310,29 +320,46 @@ bool Camera::onAdd()
    resetWorldBox();
    resetWorldBox();
 
 
    addToScene();
    addToScene();
+
+   scriptOnAdd();
+
    return true;
    return true;
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
 
-void Camera::onEditorEnable()
+void Camera::onRemove()
 {
 {
-   mNetFlags.set(Ghostable);
+   scriptOnRemove();
+   removeFromScene();
+   Parent::onRemove();
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
 
-void Camera::onEditorDisable()
+bool Camera::onNewDataBlock( GameBaseData *dptr, bool reload )
 {
 {
-   mNetFlags.clear(Ghostable);
+   mDataBlock = dynamic_cast<CameraData*>(dptr);
+   if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) )
+      return false;
+
+   scriptOnNewDataBlock();
+
+   return true;
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
 
-void Camera::onRemove()
+void Camera::onEditorEnable()
 {
 {
-   removeFromScene();
-   Parent::onRemove();
+   mNetFlags.set(Ghostable);
+}
+
+//----------------------------------------------------------------------------
+
+void Camera::onEditorDisable()
+{
+   mNetFlags.clear(Ghostable);
 }
 }
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -460,79 +487,154 @@ void Camera::processTick(const Move* move)
 
 
       VectorF rotVec(0, 0, 0);
       VectorF rotVec(0, 0, 0);
 
 
-      // process input/determine rotation vector
-      if(virtualMode != StationaryMode &&
-         virtualMode != TrackObjectMode &&
-         (!mLocked || virtualMode != OrbitObjectMode && virtualMode != OrbitPointMode))
+      bool doStandardMove = true;
+
+#ifdef TORQUE_EXTENDED_MOVE
+      GameConnection* con = getControllingClient();
+
+      // Work with an absolute rotation from the ExtendedMove class?
+      if(con && con->getControlSchemeAbsoluteRotation())
       {
       {
-         if(!strafeMode)
+         doStandardMove = false;
+         const ExtendedMove* emove = dynamic_cast<const ExtendedMove*>(move);
+         U32 emoveIndex = smExtendedMovePosRotIndex;
+         if(emoveIndex >= ExtendedMove::MaxPositionsRotations)
+            emoveIndex = 0;
+
+         if(emove->EulerBasedRotation[emoveIndex])
          {
          {
-            rotVec.x = move->pitch;
-            rotVec.z = move->yaw;
+            if(virtualMode != StationaryMode &&
+               virtualMode != TrackObjectMode &&
+               (!mLocked || virtualMode != OrbitObjectMode && virtualMode != OrbitPointMode))
+            {
+               // Pitch
+               mRot.x += (emove->rotX[emoveIndex] - mLastAbsolutePitch);
+
+               // Do we also include the relative pitch value?
+               if(con->getControlSchemeAddPitchToAbsRot() && !strafeMode)
+               {
+                  F32 x = move->pitch;
+                  if (x > M_PI_F)
+                     x -= M_2PI_F;
+
+                  mRot.x += x;
+               }
+
+               // Constrain the range of mRot.x
+               while (mRot.x < -M_PI_F) 
+                  mRot.x += M_2PI_F;
+               while (mRot.x > M_PI_F) 
+                  mRot.x -= M_2PI_F;
+
+               // Yaw
+               mRot.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
+
+               // Do we also include the relative yaw value?
+               if(con->getControlSchemeAddYawToAbsRot() && !strafeMode)
+               {
+                  F32 z = move->yaw;
+                  if (z > M_PI_F)
+                     z -= M_2PI_F;
+
+                  mRot.z += z;
+               }
+
+               // Constrain the range of mRot.z
+               while (mRot.z < -M_PI_F) 
+                  mRot.z += M_2PI_F;
+               while (mRot.z > M_PI_F) 
+                  mRot.z -= M_2PI_F;
+
+               mLastAbsoluteYaw = emove->rotZ[emoveIndex];
+               mLastAbsolutePitch = emove->rotX[emoveIndex];
+
+               // Bank
+               mRot.y = emove->rotY[emoveIndex];
+
+               // Constrain the range of mRot.y
+               while (mRot.y > M_PI_F) 
+                  mRot.y -= M_2PI_F;
+            }
          }
          }
       }
       }
-      else if(virtualMode == TrackObjectMode && bool(mOrbitObject))
+#endif
+
+      if(doStandardMove)
       {
       {
-         // orient the camera to face the object
-         Point3F objPos;
-         // If this is a shapebase, use its render eye transform
-         // to avoid jittering.
-         ShapeBase *shape = dynamic_cast<ShapeBase*>((GameBase*)mOrbitObject);
-         if( shape != NULL )
+         // process input/determine rotation vector
+         if(virtualMode != StationaryMode &&
+            virtualMode != TrackObjectMode &&
+            (!mLocked || virtualMode != OrbitObjectMode && virtualMode != OrbitPointMode))
          {
          {
-            MatrixF ret;
-            shape->getRenderEyeTransform( &ret );
-            objPos = ret.getPosition();
+            if(!strafeMode)
+            {
+               rotVec.x = move->pitch;
+               rotVec.z = move->yaw;
+            }
          }
          }
-         else
+         else if(virtualMode == TrackObjectMode && bool(mOrbitObject))
          {
          {
-            mOrbitObject->getWorldBox().getCenter(&objPos);
+            // orient the camera to face the object
+            Point3F objPos;
+            // If this is a shapebase, use its render eye transform
+            // to avoid jittering.
+            ShapeBase *shape = dynamic_cast<ShapeBase*>((GameBase*)mOrbitObject);
+            if( shape != NULL )
+            {
+               MatrixF ret;
+               shape->getRenderEyeTransform( &ret );
+               objPos = ret.getPosition();
+            }
+            else
+            {
+               mOrbitObject->getWorldBox().getCenter(&objPos);
+            }
+            mObjToWorld.getColumn(3,&pos);
+            vec = objPos - pos;
+            vec.normalizeSafe();
+            F32 pitch, yaw;
+            MathUtils::getAnglesFromVector(vec, yaw, pitch);
+            rotVec.x = -pitch - mRot.x;
+            rotVec.z = yaw - mRot.z;
+            if(rotVec.z > M_PI_F)
+               rotVec.z -= M_2PI_F;
+            else if(rotVec.z < -M_PI_F)
+               rotVec.z += M_2PI_F;
          }
          }
-         mObjToWorld.getColumn(3,&pos);
-         vec = objPos - pos;
-         vec.normalizeSafe();
-         F32 pitch, yaw;
-         MathUtils::getAnglesFromVector(vec, yaw, pitch);
-         rotVec.x = -pitch - mRot.x;
-         rotVec.z = yaw - mRot.z;
-         if(rotVec.z > M_PI_F)
-            rotVec.z -= M_2PI_F;
-         else if(rotVec.z < -M_PI_F)
-            rotVec.z += M_2PI_F;
-      }
 
 
-      // apply rotation vector according to physics rules
-      if(mNewtonRotation)
-      {
-         const F32 force = mAngularForce;
-         const F32 drag = mAngularDrag;
+         // apply rotation vector according to physics rules
+         if(mNewtonRotation)
+         {
+            const F32 force = mAngularForce;
+            const F32 drag = mAngularDrag;
 
 
-         VectorF acc(0.0f, 0.0f, 0.0f);
+            VectorF acc(0.0f, 0.0f, 0.0f);
 
 
-         rotVec.x *= 2.0f;   // Assume that our -2PI to 2PI range was clamped to -PI to PI in script
-         rotVec.z *= 2.0f;   // Assume that our -2PI to 2PI range was clamped to -PI to PI in script
+            rotVec.x *= 2.0f;   // Assume that our -2PI to 2PI range was clamped to -PI to PI in script
+            rotVec.z *= 2.0f;   // Assume that our -2PI to 2PI range was clamped to -PI to PI in script
 
 
-         F32 rotVecL = rotVec.len();
-         if(rotVecL > 0)
-         {
-            acc = (rotVec * force / mMass) * TickSec;
-         }
+            F32 rotVecL = rotVec.len();
+            if(rotVecL > 0)
+            {
+               acc = (rotVec * force / mMass) * TickSec;
+            }
 
 
-         // Accelerate
-         mAngularVelocity += acc;
+            // Accelerate
+            mAngularVelocity += acc;
 
 
-         // Drag
-         mAngularVelocity -= mAngularVelocity * drag * TickSec;
+            // Drag
+            mAngularVelocity -= mAngularVelocity * drag * TickSec;
 
 
-         // Rotate
-         mRot += mAngularVelocity * TickSec;
-         clampPitchAngle(mRot.x);
-      }
-      else
-      {
-         mRot.x += rotVec.x;
-         mRot.z += rotVec.z;
-         clampPitchAngle(mRot.x);
+            // Rotate
+            mRot += mAngularVelocity * TickSec;
+            clampPitchAngle(mRot.x);
+         }
+         else
+         {
+            mRot.x += rotVec.x;
+            mRot.z += rotVec.z;
+            clampPitchAngle(mRot.x);
+         }
       }
       }
 
 
       // Update position
       // Update position
@@ -667,6 +769,13 @@ void Camera::processTick(const Move* move)
          mDelta.rot = mRot;
          mDelta.rot = mRot;
          mDelta.posVec = mDelta.posVec - mDelta.pos;
          mDelta.posVec = mDelta.posVec - mDelta.pos;
          mDelta.rotVec = mDelta.rotVec - mDelta.rot;
          mDelta.rotVec = mDelta.rotVec - mDelta.rot;
+         for(U32 i=0; i<3; ++i)
+         {
+            if (mDelta.rotVec[i] > M_PI_F)
+               mDelta.rotVec[i] -= M_2PI_F;
+            else if (mDelta.rotVec[i] < -M_PI_F)
+               mDelta.rotVec[i] += M_2PI_F;
+         }
       }
       }
 
 
       if(mustValidateEyePoint)
       if(mustValidateEyePoint)
@@ -793,7 +902,21 @@ void Camera::_setPosition(const Point3F& pos, const Point3F& rot)
    zRot.set(EulerF(0.0f, 0.0f, rot.z));
    zRot.set(EulerF(0.0f, 0.0f, rot.z));
    
    
    MatrixF temp;
    MatrixF temp;
-   temp.mul(zRot, xRot);
+
+   if(mDataBlock->cameraCanBank)
+   {
+      // Take rot.y into account to bank the camera
+      MatrixF imat;
+      imat.mul(zRot, xRot);
+      MatrixF ymat;
+      ymat.set(EulerF(0.0f, rot.y, 0.0f));
+      temp.mul(imat, ymat);
+   }
+   else
+   {
+      temp.mul(zRot, xRot);
+   }
+
    temp.setColumn(3, pos);
    temp.setColumn(3, pos);
    Parent::setTransform(temp);
    Parent::setTransform(temp);
    mRot = rot;
    mRot = rot;
@@ -808,7 +931,21 @@ void Camera::setRotation(const Point3F& rot)
    zRot.set(EulerF(0.0f, 0.0f, rot.z));
    zRot.set(EulerF(0.0f, 0.0f, rot.z));
 
 
    MatrixF temp;
    MatrixF temp;
-   temp.mul(zRot, xRot);
+
+   if(mDataBlock->cameraCanBank)
+   {
+      // Take rot.y into account to bank the camera
+      MatrixF imat;
+      imat.mul(zRot, xRot);
+      MatrixF ymat;
+      ymat.set(EulerF(0.0f, rot.y, 0.0f));
+      temp.mul(imat, ymat);
+   }
+   else
+   {
+      temp.mul(zRot, xRot);
+   }
+
    temp.setColumn(3, getPosition());
    temp.setColumn(3, getPosition());
    Parent::setTransform(temp);
    Parent::setTransform(temp);
    mRot = rot;
    mRot = rot;
@@ -821,8 +958,25 @@ void Camera::_setRenderPosition(const Point3F& pos,const Point3F& rot)
    MatrixF xRot, zRot;
    MatrixF xRot, zRot;
    xRot.set(EulerF(rot.x, 0, 0));
    xRot.set(EulerF(rot.x, 0, 0));
    zRot.set(EulerF(0, 0, rot.z));
    zRot.set(EulerF(0, 0, rot.z));
+
    MatrixF temp;
    MatrixF temp;
-   temp.mul(zRot, xRot);
+
+   // mDataBlock may not be defined yet as this method is called during
+   // SceneObject::onAdd().
+   if(mDataBlock && mDataBlock->cameraCanBank)
+   {
+      // Take rot.y into account to bank the camera
+      MatrixF imat;
+      imat.mul(zRot, xRot);
+      MatrixF ymat;
+      ymat.set(EulerF(0.0f, rot.y, 0.0f));
+      temp.mul(imat, ymat);
+   }
+   else
+   {
+      temp.mul(zRot, xRot);
+   }
+
    temp.setColumn(3, pos);
    temp.setColumn(3, pos);
    Parent::setRenderTransform(temp);
    Parent::setRenderTransform(temp);
 }
 }
@@ -839,6 +993,11 @@ void Camera::writePacketData(GameConnection *connection, BitStream *bstream)
    bstream->setCompressionPoint(pos);
    bstream->setCompressionPoint(pos);
    mathWrite(*bstream, pos);
    mathWrite(*bstream, pos);
    bstream->write(mRot.x);
    bstream->write(mRot.x);
+   if(bstream->writeFlag(mDataBlock->cameraCanBank))
+   {
+      // Include mRot.y to allow for camera banking
+      bstream->write(mRot.y);
+   }
    bstream->write(mRot.z);
    bstream->write(mRot.z);
 
 
    U32 writeMode = mMode;
    U32 writeMode = mMode;
@@ -912,6 +1071,11 @@ void Camera::readPacketData(GameConnection *connection, BitStream *bstream)
    mathRead(*bstream, &pos);
    mathRead(*bstream, &pos);
    bstream->setCompressionPoint(pos);
    bstream->setCompressionPoint(pos);
    bstream->read(&rot.x);
    bstream->read(&rot.x);
+   if(bstream->readFlag())
+   {
+      // Include rot.y to allow for camera banking
+      bstream->read(&rot.y);
+   }
    bstream->read(&rot.z);
    bstream->read(&rot.z);
 
 
    GameBase* obj = 0;
    GameBase* obj = 0;
@@ -1184,6 +1348,11 @@ void Camera::consoleInit()
       "- Fly Mode\n"
       "- Fly Mode\n"
       "- Overhead Mode\n"
       "- Overhead Mode\n"
       "@ingroup BaseCamera\n");
       "@ingroup BaseCamera\n");
+
+   // ExtendedMove support
+   Con::addVariable("$camera::extendedMovePosRotIndex", TypeS32, &smExtendedMovePosRotIndex, 
+      "@brief The ExtendedMove position/rotation index used for camera movements.\n\n"
+	   "@ingroup BaseCamera\n");
 }
 }
 
 
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------

+ 9 - 0
Engine/source/T3D/camera.h

@@ -73,6 +73,9 @@ class Camera: public ShapeBase
          CameraLastMode  = EditOrbitMode
          CameraLastMode  = EditOrbitMode
       };
       };
 
 
+      /// The ExtendedMove position/rotation index used for camera movements
+      static S32 smExtendedMovePosRotIndex;
+
    protected:
    protected:
 
 
       enum MaskBits
       enum MaskBits
@@ -92,6 +95,8 @@ class Camera: public ShapeBase
          VectorF rotVec;
          VectorF rotVec;
       };
       };
 
 
+      CameraData* mDataBlock;
+
       Point3F mRot;
       Point3F mRot;
       StateDelta mDelta;
       StateDelta mDelta;
 
 
@@ -106,6 +111,9 @@ class Camera: public ShapeBase
       Point3F mPosition;
       Point3F mPosition;
       bool mObservingClientObject;
       bool mObservingClientObject;
 
 
+      F32 mLastAbsoluteYaw;            ///< Stores that last absolute yaw value as passed in by ExtendedMove
+      F32 mLastAbsolutePitch;          ///< Stores that last absolute pitch value as passed in by ExtendedMove
+
       /// @name NewtonFlyMode
       /// @name NewtonFlyMode
       /// @{
       /// @{
 
 
@@ -223,6 +231,7 @@ class Camera: public ShapeBase
 
 
       virtual bool onAdd();
       virtual bool onAdd();
       virtual void onRemove();
       virtual void onRemove();
+      virtual bool onNewDataBlock( GameBaseData *dptr, bool reload );
       virtual void processTick( const Move* move );
       virtual void processTick( const Move* move );
       virtual void interpolateTick( F32 delta);
       virtual void interpolateTick( F32 delta);
       virtual void getCameraTransform( F32* pos,MatrixF* mat );
       virtual void getCameraTransform( F32* pos,MatrixF* mat );

+ 81 - 0
Engine/source/T3D/gameBase/gameConnection.cpp

@@ -219,6 +219,13 @@ GameConnection::GameConnection()
    // first person
    // first person
    mFirstPerson = true;
    mFirstPerson = true;
    mUpdateFirstPerson = false;
    mUpdateFirstPerson = false;
+
+   // Control scheme
+   mUpdateControlScheme = false;
+   mAbsoluteRotation = false;
+   mAddYawToAbsRot = false;
+   mAddPitchToAbsRot = false;
+
    clearDisplayDevice();
    clearDisplayDevice();
 }
 }
 
 
@@ -743,7 +750,15 @@ void GameConnection::setFirstPerson(bool firstPerson)
    mUpdateFirstPerson = true;
    mUpdateFirstPerson = true;
 }
 }
 
 
+//----------------------------------------------------------------------------
 
 
+void GameConnection::setControlSchemeParameters(bool absoluteRotation, bool addYawToAbsRot, bool addPitchToAbsRot)
+{
+   mAbsoluteRotation = absoluteRotation;
+   mAddYawToAbsRot = addYawToAbsRot;
+   mAddPitchToAbsRot = addPitchToAbsRot;
+   mUpdateControlScheme = true;
+}
 
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
 
@@ -826,6 +841,11 @@ void GameConnection::writeDemoStartBlock(ResizeBitStream *stream)
    stream->write(mCameraPos);
    stream->write(mCameraPos);
    stream->write(mCameraSpeed);
    stream->write(mCameraSpeed);
 
 
+   // Control scheme
+   stream->write(mAbsoluteRotation);
+   stream->write(mAddYawToAbsRot);
+   stream->write(mAddPitchToAbsRot);
+
    stream->writeString(Con::getVariable("$Client::MissionFile"));
    stream->writeString(Con::getVariable("$Client::MissionFile"));
 
 
    mMoveList->writeDemoStartBlock(stream);
    mMoveList->writeDemoStartBlock(stream);
@@ -902,6 +922,11 @@ bool GameConnection::readDemoStartBlock(BitStream *stream)
    stream->read(&mCameraPos);
    stream->read(&mCameraPos);
    stream->read(&mCameraSpeed);
    stream->read(&mCameraSpeed);
 
 
+   // Control scheme
+   stream->read(&mAbsoluteRotation);
+   stream->read(&mAddYawToAbsRot);
+   stream->read(&mAddPitchToAbsRot);
+
    char buf[256];
    char buf[256];
    stream->readString(buf);
    stream->readString(buf);
    Con::setVariable("$Client::MissionFile",buf);
    Con::setVariable("$Client::MissionFile",buf);
@@ -1078,6 +1103,16 @@ void GameConnection::readPacket(BitStream *bstream)
       else
       else
          setCameraObject(0);
          setCameraObject(0);
 
 
+      // server changed control scheme
+      if(bstream->readFlag())
+      {
+         bool absoluteRotation = bstream->readFlag();
+         bool addYawToAbsRot = bstream->readFlag();
+         bool addPitchToAbsRot = bstream->readFlag();
+         setControlSchemeParameters(absoluteRotation, addYawToAbsRot, addPitchToAbsRot);
+         mUpdateControlScheme = false;
+      }
+
       // server changed first person
       // server changed first person
       if(bstream->readFlag())
       if(bstream->readFlag())
       {
       {
@@ -1108,6 +1143,16 @@ void GameConnection::readPacket(BitStream *bstream)
       if (bstream->readFlag())
       if (bstream->readFlag())
          mControlForceMismatch = true;
          mControlForceMismatch = true;
 
 
+      // client changed control scheme
+      if(bstream->readFlag())
+      {
+         bool absoluteRotation = bstream->readFlag();
+         bool addYawToAbsRot = bstream->readFlag();
+         bool addPitchToAbsRot = bstream->readFlag();
+         setControlSchemeParameters(absoluteRotation, addYawToAbsRot, addPitchToAbsRot);
+         mUpdateControlScheme = false;
+      }
+
       // client changed first person
       // client changed first person
       if(bstream->readFlag())
       if(bstream->readFlag())
       {
       {
@@ -1170,6 +1215,15 @@ void GameConnection::writePacket(BitStream *bstream, PacketNotify *note)
       }
       }
       bstream->writeFlag(forceUpdate);
       bstream->writeFlag(forceUpdate);
 
 
+      // Control scheme changed?
+      if(bstream->writeFlag(mUpdateControlScheme))
+      {
+         bstream->writeFlag(mAbsoluteRotation);
+         bstream->writeFlag(mAddYawToAbsRot);
+         bstream->writeFlag(mAddPitchToAbsRot);
+         mUpdateControlScheme = false;
+      }
+
       // first person changed?
       // first person changed?
       if(bstream->writeFlag(mUpdateFirstPerson)) 
       if(bstream->writeFlag(mUpdateFirstPerson)) 
       {
       {
@@ -1258,6 +1312,15 @@ void GameConnection::writePacket(BitStream *bstream, PacketNotify *note)
       else
       else
          bstream->writeFlag( false );
          bstream->writeFlag( false );
 
 
+      // Control scheme changed?
+      if(bstream->writeFlag(mUpdateControlScheme))
+      {
+         bstream->writeFlag(mAbsoluteRotation);
+         bstream->writeFlag(mAddYawToAbsRot);
+         bstream->writeFlag(mAddPitchToAbsRot);
+         mUpdateControlScheme = false;
+      }
+
       // first person changed?
       // first person changed?
       if(bstream->writeFlag(mUpdateFirstPerson)) 
       if(bstream->writeFlag(mUpdateFirstPerson)) 
       {
       {
@@ -2115,3 +2178,21 @@ DefineEngineMethod( GameConnection, setFirstPerson, void, (bool firstPerson),,
 {
 {
    object->setFirstPerson(firstPerson);
    object->setFirstPerson(firstPerson);
 }
 }
+
+DefineEngineMethod( GameConnection, setControlSchemeParameters, void, (bool absoluteRotation, bool addYawToAbsRot, bool addPitchToAbsRot),,
+   "@brief Set the control scheme that may be used by a connection's control object.\n\n"
+   
+   "@param absoluteRotation Use absolute rotation values from client, likely through ExtendedMove.\n"
+   "@param addYawToAbsRot Add relative yaw control to the absolute rotation calculation.  Only useful when absoluteRotation is true.\n\n" )
+{
+   object->setControlSchemeParameters(absoluteRotation, addYawToAbsRot, addPitchToAbsRot);
+}
+
+DefineEngineMethod( GameConnection, getControlSchemeAbsoluteRotation, bool, (),,
+   "@brief Get the connection's control scheme absolute rotation property.\n\n"
+   
+   "@return True if the connection's control object should use an absolute rotation control scheme.\n\n"
+   "@see GameConnection::setControlSchemeParameters()\n\n")
+{
+   return object->getControlSchemeAbsoluteRotation();
+}

+ 14 - 0
Engine/source/T3D/gameBase/gameConnection.h

@@ -91,6 +91,14 @@ private:
    IDisplayDevice* mDisplayDevice;  ///< Optional client display device that imposes rendering properties.
    IDisplayDevice* mDisplayDevice;  ///< Optional client display device that imposes rendering properties.
    /// @}
    /// @}
 
 
+   /// @name Client side control scheme that may be referenced by control objects
+   /// @{
+   bool  mUpdateControlScheme;   ///< Set to notify client or server of control scheme change
+   bool  mAbsoluteRotation;      ///< Use absolute rotation values from client, likely through ExtendedMove
+   bool  mAddYawToAbsRot;        ///< Add relative yaw control to the absolute rotation calculation.  Only useful with mAbsoluteRotation.
+   bool  mAddPitchToAbsRot;      ///< Add relative pitch control to the absolute rotation calculation.  Only useful with mAbsoluteRotation.
+   /// @}
+
 public:
 public:
 
 
    /// @name Protocol Versions
    /// @name Protocol Versions
@@ -270,6 +278,12 @@ public:
    const IDisplayDevice* getDisplayDevice() const { return mDisplayDevice; }
    const IDisplayDevice* getDisplayDevice() const { return mDisplayDevice; }
    void setDisplayDevice(IDisplayDevice* display) { mDisplayDevice = display; }
    void setDisplayDevice(IDisplayDevice* display) { mDisplayDevice = display; }
    void clearDisplayDevice() { mDisplayDevice = NULL; }
    void clearDisplayDevice() { mDisplayDevice = NULL; }
+
+   void setControlSchemeParameters(bool absoluteRotation, bool addYawToAbsRot, bool addPitchToAbsRot);
+   bool getControlSchemeAbsoluteRotation() {return mAbsoluteRotation;}
+   bool getControlSchemeAddYawToAbsRot() {return mAddYawToAbsRot;}
+   bool getControlSchemeAddPitchToAbsRot() {return mAddPitchToAbsRot;}
+
    /// @}
    /// @}
 
 
    void detectLag();
    void detectLag();

+ 182 - 35
Engine/source/T3D/player.cpp

@@ -57,6 +57,10 @@
 #include "T3D/decal/decalData.h"
 #include "T3D/decal/decalData.h"
 #include "materials/baseMatInstance.h"
 #include "materials/baseMatInstance.h"
 
 
+#ifdef TORQUE_EXTENDED_MOVE
+   #include "T3D/gameBase/extended/extendedMove.h"
+#endif
+
 // Amount of time if takes to transition to a new action sequence.
 // Amount of time if takes to transition to a new action sequence.
 static F32 sAnimationTransitionTime = 0.25f;
 static F32 sAnimationTransitionTime = 0.25f;
 static bool sUseAnimationTransitions = true;
 static bool sUseAnimationTransitions = true;
@@ -96,6 +100,8 @@ static F32 sMinWarpTicks = 0.5f;       // Fraction of tick at which instant warp
 static S32 sMaxWarpTicks = 3;          // Max warp duration in ticks
 static S32 sMaxWarpTicks = 3;          // Max warp duration in ticks
 static S32 sMaxPredictionTicks = 30;   // Number of ticks to predict
 static S32 sMaxPredictionTicks = 30;   // Number of ticks to predict
 
 
+S32 Player::smExtendedMoveHeadPosRotIndex = 0;  // The ExtendedMove position/rotation index used for head movements
+
 // Anchor point compression
 // Anchor point compression
 const F32 sAnchorMaxDistance = 32.0f;
 const F32 sAnchorMaxDistance = 32.0f;
 
 
@@ -1650,6 +1656,9 @@ Player::Player()
       mShapeFPFlashThread[i] = 0;
       mShapeFPFlashThread[i] = 0;
       mShapeFPSpinThread[i] = 0;
       mShapeFPSpinThread[i] = 0;
    }
    }
+
+   mLastAbsoluteYaw = 0.0f;
+   mLastAbsolutePitch = 0.0f;
 }
 }
 
 
 Player::~Player()
 Player::~Player()
@@ -2523,38 +2532,130 @@ void Player::updateMove(const Move* move)
       F32 prevZRot = mRot.z;
       F32 prevZRot = mRot.z;
       delta.headVec = mHead;
       delta.headVec = mHead;
 
 
-      F32 p = move->pitch * (mPose == SprintPose ? mDataBlock->sprintPitchScale : 1.0f);
-      if (p > M_PI_F) 
-         p -= M_2PI_F;
-      mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
-                        mDataBlock->maxLookAngle);
-
-      F32 y = move->yaw * (mPose == SprintPose ? mDataBlock->sprintYawScale : 1.0f);
-      if (y > M_PI_F)
-         y -= M_2PI_F;
-
+      bool doStandardMove = true;
       GameConnection* con = getControllingClient();
       GameConnection* con = getControllingClient();
-      if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
+
+#ifdef TORQUE_EXTENDED_MOVE
+      // Work with an absolute rotation from the ExtendedMove class?
+      if(con && con->getControlSchemeAbsoluteRotation())
       {
       {
-         mHead.z = mClampF(mHead.z + y,
-                           -mDataBlock->maxFreelookAngle,
-                           mDataBlock->maxFreelookAngle);
+         doStandardMove = false;
+         const ExtendedMove* emove = dynamic_cast<const ExtendedMove*>(move);
+         U32 emoveIndex = smExtendedMoveHeadPosRotIndex;
+         if(emoveIndex >= ExtendedMove::MaxPositionsRotations)
+            emoveIndex = 0;
+
+         if(emove->EulerBasedRotation[emoveIndex])
+         {
+            // Head pitch
+            mHead.x += (emove->rotX[emoveIndex] - mLastAbsolutePitch);
+
+            // Do we also include the relative yaw value?
+            if(con->getControlSchemeAddPitchToAbsRot())
+            {
+               F32 x = move->pitch;
+               if (x > M_PI_F)
+                  x -= M_2PI_F;
+
+               mHead.x += x;
+            }
+
+            // Constrain the range of mHead.x
+            while (mHead.x < -M_PI_F) 
+               mHead.x += M_2PI_F;
+            while (mHead.x > M_PI_F) 
+               mHead.x -= M_2PI_F;
+
+            // Rotate (heading) head or body?
+            if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
+            {
+               // Rotate head
+               mHead.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
+
+               // Do we also include the relative yaw value?
+               if(con->getControlSchemeAddYawToAbsRot())
+               {
+                  F32 z = move->yaw;
+                  if (z > M_PI_F)
+                     z -= M_2PI_F;
+
+                  mHead.z += z;
+               }
+
+               // Constrain the range of mHead.z
+               while (mHead.z < 0.0f)
+                  mHead.z += M_2PI_F;
+               while (mHead.z > M_2PI_F)
+                  mHead.z -= M_2PI_F;
+            }
+            else
+            {
+               // Rotate body
+               mRot.z += (emove->rotZ[emoveIndex] - mLastAbsoluteYaw);
+
+               // Do we also include the relative yaw value?
+               if(con->getControlSchemeAddYawToAbsRot())
+               {
+                  F32 z = move->yaw;
+                  if (z > M_PI_F)
+                     z -= M_2PI_F;
+
+                  mRot.z += z;
+               }
+
+               // Constrain the range of mRot.z
+               while (mRot.z < 0.0f)
+                  mRot.z += M_2PI_F;
+               while (mRot.z > M_2PI_F)
+                  mRot.z -= M_2PI_F;
+            }
+            mLastAbsoluteYaw = emove->rotZ[emoveIndex];
+            mLastAbsolutePitch = emove->rotX[emoveIndex];
+
+            // Head bank
+            mHead.y = emove->rotY[emoveIndex];
+
+            // Constrain the range of mHead.y
+            while (mHead.y > M_PI_F) 
+               mHead.y -= M_2PI_F;
+         }
       }
       }
-      else
+#endif
+
+      if(doStandardMove)
       {
       {
-         mRot.z += y;
-         // Rotate the head back to the front, center horizontal
-         // as well if we're controlling another object.
-         mHead.z *= 0.5f;
-         if (mControlObject)
-            mHead.x *= 0.5f;
-      }
+         F32 p = move->pitch * (mPose == SprintPose ? mDataBlock->sprintPitchScale : 1.0f);
+         if (p > M_PI_F) 
+            p -= M_2PI_F;
+         mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
+                           mDataBlock->maxLookAngle);
+
+         F32 y = move->yaw * (mPose == SprintPose ? mDataBlock->sprintYawScale : 1.0f);
+         if (y > M_PI_F)
+            y -= M_2PI_F;
+
+         if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
+         {
+            mHead.z = mClampF(mHead.z + y,
+                              -mDataBlock->maxFreelookAngle,
+                              mDataBlock->maxFreelookAngle);
+         }
+         else
+         {
+            mRot.z += y;
+            // Rotate the head back to the front, center horizontal
+            // as well if we're controlling another object.
+            mHead.z *= 0.5f;
+            if (mControlObject)
+               mHead.x *= 0.5f;
+         }
 
 
-      // constrain the range of mRot.z
-      while (mRot.z < 0.0f)
-         mRot.z += M_2PI_F;
-      while (mRot.z > M_2PI_F)
-         mRot.z -= M_2PI_F;
+         // constrain the range of mRot.z
+         while (mRot.z < 0.0f)
+            mRot.z += M_2PI_F;
+         while (mRot.z > M_2PI_F)
+            mRot.z -= M_2PI_F;
+      }
 
 
       delta.rot = mRot;
       delta.rot = mRot;
       delta.rotVec.x = delta.rotVec.y = 0.0f;
       delta.rotVec.x = delta.rotVec.y = 0.0f;
@@ -2566,6 +2667,13 @@ void Player::updateMove(const Move* move)
 
 
       delta.head = mHead;
       delta.head = mHead;
       delta.headVec -= mHead;
       delta.headVec -= mHead;
+      for(U32 i=0; i<3; ++i)
+      {
+         if (delta.headVec[i] > M_PI_F)
+            delta.headVec[i] -= M_2PI_F;
+         else if (delta.headVec[i] < -M_PI_F)
+            delta.headVec[i] += M_2PI_F;
+      }
    }
    }
    MatrixF zRot;
    MatrixF zRot;
    zRot.set(EulerF(0.0f, 0.0f, mRot.z));
    zRot.set(EulerF(0.0f, 0.0f, mRot.z));
@@ -5259,7 +5367,7 @@ void Player::setTransform(const MatrixF& mat)
 
 
 void Player::getEyeTransform(MatrixF* mat)
 void Player::getEyeTransform(MatrixF* mat)
 {
 {
-   getEyeBaseTransform(mat);
+   getEyeBaseTransform(mat, true);
 
 
    // The shape instance is animated in getEyeBaseTransform() so we're
    // The shape instance is animated in getEyeBaseTransform() so we're
    // good here when attempting to get the eye node position on the server.
    // good here when attempting to get the eye node position on the server.
@@ -5297,7 +5405,7 @@ void Player::getEyeTransform(MatrixF* mat)
    }
    }
 }
 }
 
 
-void Player::getEyeBaseTransform(MatrixF* mat)
+void Player::getEyeBaseTransform(MatrixF* mat, bool includeBank)
 {
 {
    // Eye transform in world space.  We only use the eye position
    // Eye transform in world space.  We only use the eye position
    // from the animation and supply our own rotation.
    // from the animation and supply our own rotation.
@@ -5313,7 +5421,19 @@ void Player::getEyeBaseTransform(MatrixF* mat)
    else
    else
       zmat.identity();
       zmat.identity();
 
 
-   pmat.mul(zmat,xmat);
+   if(includeBank && mDataBlock->cameraCanBank)
+   {
+      // Take mHead.y into account to bank the camera
+      MatrixF imat;
+      imat.mul(zmat, xmat);
+      MatrixF ymat;
+      ymat.set(EulerF(0.0f, mHead.y, 0.0f));
+      pmat.mul(imat, ymat);
+   }
+   else
+   {
+      pmat.mul(zmat,xmat);
+   }
 
 
    F32 *dp = pmat;
    F32 *dp = pmat;
 
 
@@ -5340,7 +5460,7 @@ void Player::getEyeBaseTransform(MatrixF* mat)
 
 
 void Player::getRenderEyeTransform(MatrixF* mat)
 void Player::getRenderEyeTransform(MatrixF* mat)
 {
 {
-   getRenderEyeBaseTransform(mat);
+   getRenderEyeBaseTransform(mat, true);
 
 
    // Use the first image that is set to use the eye node
    // Use the first image that is set to use the eye node
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
    for (U32 i=0; i<ShapeBase::MaxMountedImages; ++i)
@@ -5369,7 +5489,7 @@ void Player::getRenderEyeTransform(MatrixF* mat)
    }
    }
 }
 }
 
 
-void Player::getRenderEyeBaseTransform(MatrixF* mat)
+void Player::getRenderEyeBaseTransform(MatrixF* mat, bool includeBank)
 {
 {
    // Eye transform in world space.  We only use the eye position
    // Eye transform in world space.  We only use the eye position
    // from the animation and supply our own rotation.
    // from the animation and supply our own rotation.
@@ -5381,7 +5501,19 @@ void Player::getRenderEyeBaseTransform(MatrixF* mat)
    else
    else
       zmat.identity();
       zmat.identity();
 
 
-   pmat.mul(zmat,xmat);
+   if(includeBank && mDataBlock->cameraCanBank)
+   {
+      // Take mHead.y delta into account to bank the camera
+      MatrixF imat;
+      imat.mul(zmat, xmat);
+      MatrixF ymat;
+      ymat.set(EulerF(0.0f, delta.head.y + delta.headVec.y * delta.dt, 0.0f));
+      pmat.mul(imat, ymat);
+   }
+   else
+   {
+      pmat.mul(zmat,xmat);
+   }
 
 
    F32 *dp = pmat;
    F32 *dp = pmat;
 
 
@@ -5539,7 +5671,7 @@ void Player::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRend
       if (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1)
       if (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1)
       {
       {
          MatrixF nmat;
          MatrixF nmat;
-         getRenderEyeBaseTransform(&nmat);
+         getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank);
          MatrixF offsetMat = image.shapeInstance[imageShapeIndex]->mNodeTransforms[data.eyeMountNode[imageShapeIndex]];
          MatrixF offsetMat = image.shapeInstance[imageShapeIndex]->mNodeTransforms[data.eyeMountNode[imageShapeIndex]];
          offsetMat.affineInverse();
          offsetMat.affineInverse();
          world.mul(nmat,offsetMat);
          world.mul(nmat,offsetMat);
@@ -5547,7 +5679,7 @@ void Player::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRend
       else
       else
       {
       {
          MatrixF nmat;
          MatrixF nmat;
-         getRenderEyeBaseTransform(&nmat);
+         getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank);
          world.mul(nmat,data.eyeOffset);
          world.mul(nmat,data.eyeOffset);
       }
       }
 
 
@@ -5866,6 +5998,11 @@ void Player::writePacketData(GameConnection *connection, BitStream *stream)
       }
       }
    }
    }
    stream->write(mHead.x);
    stream->write(mHead.x);
+   if(stream->writeFlag(mDataBlock->cameraCanBank))
+   {
+      // Include mHead.y to allow for camera banking
+      stream->write(mHead.y);
+   }
    stream->write(mHead.z);
    stream->write(mHead.z);
    stream->write(mRot.z);
    stream->write(mRot.z);
 
 
@@ -5928,6 +6065,11 @@ void Player::readPacketData(GameConnection *connection, BitStream *stream)
    else
    else
       pos = delta.pos;
       pos = delta.pos;
    stream->read(&mHead.x);
    stream->read(&mHead.x);
+   if(stream->readFlag())
+   {
+      // Include mHead.y to allow for camera banking
+      stream->read(&mHead.y);
+   }
    stream->read(&mHead.z);
    stream->read(&mHead.z);
    stream->read(&rot.z);
    stream->read(&rot.z);
    rot.x = rot.y = 0;
    rot.x = rot.y = 0;
@@ -6587,6 +6729,11 @@ void Player::consoleInit()
    Con::addVariable("$player::vehicleDismountTrigger", TypeS32, &sVehicleDismountTrigger, 
    Con::addVariable("$player::vehicleDismountTrigger", TypeS32, &sVehicleDismountTrigger, 
       "@brief The move trigger index used to dismount player.\n\n"
       "@brief The move trigger index used to dismount player.\n\n"
 	   "@ingroup GameObjects\n");
 	   "@ingroup GameObjects\n");
+
+   // ExtendedMove support
+   Con::addVariable("$player::extendedMoveHeadPosRotIndex", TypeS32, &smExtendedMoveHeadPosRotIndex, 
+      "@brief The ExtendedMove position/rotation index used for head movements.\n\n"
+	   "@ingroup GameObjects\n");
 }
 }
 
 
 //--------------------------------------------------------------------------
 //--------------------------------------------------------------------------

+ 8 - 2
Engine/source/T3D/player.h

@@ -385,6 +385,9 @@ public:
       NumPoseBits = 3
       NumPoseBits = 3
    };
    };
 
 
+   /// The ExtendedMove position/rotation index used for head movements
+   static S32 smExtendedMoveHeadPosRotIndex;
+
 protected:
 protected:
 
 
    /// Bit masks for different types of events
    /// Bit masks for different types of events
@@ -444,6 +447,9 @@ protected:
 
 
    bool mUseHeadZCalc;              ///< Including mHead.z in transform calculations
    bool mUseHeadZCalc;              ///< Including mHead.z in transform calculations
 
 
+   F32 mLastAbsoluteYaw;            ///< Stores that last absolute yaw value as passed in by ExtendedMove
+   F32 mLastAbsolutePitch;          ///< Stores that last absolute pitch value as passed in by ExtendedMove
+
    S32 mMountPending;               ///< mMountPending suppresses tickDelay countdown so players will sit until
    S32 mMountPending;               ///< mMountPending suppresses tickDelay countdown so players will sit until
                                     ///< their mount, or another animation, comes through (or 13 seconds elapses).
                                     ///< their mount, or another animation, comes through (or 13 seconds elapses).
 
 
@@ -687,9 +693,9 @@ public:
 
 
    void setTransform(const MatrixF &mat);
    void setTransform(const MatrixF &mat);
    void getEyeTransform(MatrixF* mat);
    void getEyeTransform(MatrixF* mat);
-   void getEyeBaseTransform(MatrixF* mat);
+   void getEyeBaseTransform(MatrixF* mat, bool includeBank);
    void getRenderEyeTransform(MatrixF* mat);
    void getRenderEyeTransform(MatrixF* mat);
-   void getRenderEyeBaseTransform(MatrixF* mat);
+   void getRenderEyeBaseTransform(MatrixF* mat, bool includeBank);
    void getCameraParameters(F32 *min, F32 *max, Point3F *offset, MatrixF *rot);
    void getCameraParameters(F32 *min, F32 *max, Point3F *offset, MatrixF *rot);
    void getMuzzleTransform(U32 imageSlot,MatrixF* mat);
    void getMuzzleTransform(U32 imageSlot,MatrixF* mat);
    void getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat);   
    void getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat);   

+ 15 - 4
Engine/source/T3D/shapeBase.cpp

@@ -171,6 +171,8 @@ ShapeBaseData::ShapeBaseData()
    cameraDefaultFov( 75.0f ),
    cameraDefaultFov( 75.0f ),
    cameraMinFov( 5.0f ),
    cameraMinFov( 5.0f ),
    cameraMaxFov( 120.f ),
    cameraMaxFov( 120.f ),
+   cameraCanBank( false ),
+   mountedImagesBank( false ),
    isInvincible( false ),
    isInvincible( false ),
    renderWhenDestroyed( true ),
    renderWhenDestroyed( true ),
    debris( NULL ),
    debris( NULL ),
@@ -544,6 +546,10 @@ void ShapeBaseData::initPersistFields()
          "The minimum camera vertical FOV allowed in degrees." );
          "The minimum camera vertical FOV allowed in degrees." );
       addField( "cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData),
       addField( "cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData),
          "The maximum camera vertical FOV allowed in degrees." );
          "The maximum camera vertical FOV allowed in degrees." );
+      addField( "cameraCanBank", TypeBool, Offset(cameraCanBank, ShapeBaseData),
+         "If the derrived class supports it, allow the camera to bank." );
+      addField( "mountedImagesBank", TypeBool, Offset(mountedImagesBank, ShapeBaseData),
+         "Do mounted images bank along with the camera?" );
       addField( "firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData),
       addField( "firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData),
          "Flag controlling whether the view from this object is first person "
          "Flag controlling whether the view from this object is first person "
          "only." );
          "only." );
@@ -689,6 +695,8 @@ void ShapeBaseData::packData(BitStream* stream)
       stream->write(cameraMinFov);
       stream->write(cameraMinFov);
    if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov))
    if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov))
       stream->write(cameraMaxFov);
       stream->write(cameraMaxFov);
+   stream->writeFlag(cameraCanBank);
+   stream->writeFlag(mountedImagesBank);
    stream->writeString( debrisShapeName );
    stream->writeString( debrisShapeName );
 
 
    stream->writeFlag(observeThroughObject);
    stream->writeFlag(observeThroughObject);
@@ -788,6 +796,9 @@ void ShapeBaseData::unpackData(BitStream* stream)
    else
    else
       cameraMaxFov = gShapeBaseDataProto.cameraMaxFov;
       cameraMaxFov = gShapeBaseDataProto.cameraMaxFov;
 
 
+   cameraCanBank = stream->readFlag();
+   mountedImagesBank = stream->readFlag();
+
    debrisShapeName = stream->readSTString();
    debrisShapeName = stream->readSTString();
 
 
    observeThroughObject = stream->readFlag();
    observeThroughObject = stream->readFlag();
@@ -1872,10 +1883,10 @@ Point3F ShapeBase::getAIRepairPoint()
 
 
 void ShapeBase::getEyeTransform(MatrixF* mat)
 void ShapeBase::getEyeTransform(MatrixF* mat)
 {
 {
-   getEyeBaseTransform(mat);
+   getEyeBaseTransform(mat, true);
 }
 }
 
 
-void ShapeBase::getEyeBaseTransform(MatrixF* mat)
+void ShapeBase::getEyeBaseTransform(MatrixF* mat, bool includeBank)
 {
 {
    // Returns eye to world space transform
    // Returns eye to world space transform
    S32 eyeNode = mDataBlock->eyeNode;
    S32 eyeNode = mDataBlock->eyeNode;
@@ -1887,10 +1898,10 @@ void ShapeBase::getEyeBaseTransform(MatrixF* mat)
 
 
 void ShapeBase::getRenderEyeTransform(MatrixF* mat)
 void ShapeBase::getRenderEyeTransform(MatrixF* mat)
 {
 {
-   getRenderEyeBaseTransform(mat);
+   getRenderEyeBaseTransform(mat, true);
 }
 }
 
 
-void ShapeBase::getRenderEyeBaseTransform(MatrixF* mat)
+void ShapeBase::getRenderEyeBaseTransform(MatrixF* mat, bool includeBank)
 {
 {
    // Returns eye to world space transform
    // Returns eye to world space transform
    S32 eyeNode = mDataBlock->eyeNode;
    S32 eyeNode = mDataBlock->eyeNode;

+ 8 - 2
Engine/source/T3D/shapeBase.h

@@ -578,6 +578,12 @@ public:
    F32 cameraMaxFov;                ///< Max vertical FOV allowed in degrees.
    F32 cameraMaxFov;                ///< Max vertical FOV allowed in degrees.
    /// @}
    /// @}
 
 
+   /// @name Camera Misc
+   /// @{
+   bool cameraCanBank;              ///< If the derrived class supports it, allow the camera to bank
+   bool mountedImagesBank;          ///< Do mounted images bank along with the camera?
+   /// @}
+
    /// @name Data initialized on preload
    /// @name Data initialized on preload
    /// @{
    /// @{
 
 
@@ -1618,7 +1624,7 @@ public:
 
 
    /// Returns the eye transform of this shape without including mounted images, IE the eyes of a player
    /// Returns the eye transform of this shape without including mounted images, IE the eyes of a player
    /// @param   mat   Eye transform (out)
    /// @param   mat   Eye transform (out)
-   virtual void getEyeBaseTransform(MatrixF* mat);
+   virtual void getEyeBaseTransform(MatrixF* mat, bool includeBank);
 
 
    /// The retraction transform is the muzzle transform in world space.
    /// The retraction transform is the muzzle transform in world space.
    ///
    ///
@@ -1671,7 +1677,7 @@ public:
    virtual void getRenderMuzzleVector(U32 imageSlot,VectorF* vec);
    virtual void getRenderMuzzleVector(U32 imageSlot,VectorF* vec);
    virtual void getRenderMuzzlePoint(U32 imageSlot,Point3F* pos);
    virtual void getRenderMuzzlePoint(U32 imageSlot,Point3F* pos);
    virtual void getRenderEyeTransform(MatrixF* mat);
    virtual void getRenderEyeTransform(MatrixF* mat);
-   virtual void getRenderEyeBaseTransform(MatrixF* mat);
+   virtual void getRenderEyeBaseTransform(MatrixF* mat, bool includeBank);
    /// @}
    /// @}
 
 
 
 

+ 4 - 4
Engine/source/T3D/shapeImage.cpp

@@ -1861,7 +1861,7 @@ void ShapeBase::getImageTransform(U32 imageSlot,MatrixF* mat)
          // We need to animate, even on the server, to make sure the nodes are in the correct location.
          // We need to animate, even on the server, to make sure the nodes are in the correct location.
          image.shapeInstance[shapeIndex]->animate();
          image.shapeInstance[shapeIndex]->animate();
 
 
-         getEyeBaseTransform(&nmat);
+         getEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank);
 
 
          MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
          MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
 
 
@@ -1900,7 +1900,7 @@ void ShapeBase::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
             image.shapeInstance[shapeIndex]->animate();
             image.shapeInstance[shapeIndex]->animate();
 
 
             MatrixF emat;
             MatrixF emat;
-            getEyeBaseTransform(&emat);
+            getEyeBaseTransform(&emat, mDataBlock->mountedImagesBank);
 
 
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             mountTransform.affineInverse();
             mountTransform.affineInverse();
@@ -1985,7 +1985,7 @@ void ShapeBase::getRenderImageTransform( U32 imageSlot, MatrixF* mat, bool noEye
 
 
       MatrixF nmat;
       MatrixF nmat;
       if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) {
       if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) {
-         getRenderEyeBaseTransform(&nmat);
+         getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank);
 
 
          MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
          MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
 
 
@@ -2023,7 +2023,7 @@ void ShapeBase::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
          if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 )
          if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 )
          {
          {
             MatrixF emat;
             MatrixF emat;
-            getRenderEyeBaseTransform(&emat);
+            getRenderEyeBaseTransform(&emat, mDataBlock->mountedImagesBank);
 
 
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             mountTransform.affineInverse();
             mountTransform.affineInverse();

+ 2 - 2
Engine/source/T3D/turret/turretShape.cpp

@@ -1270,7 +1270,7 @@ void TurretShape::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
             image.shapeInstance[shapeIndex]->animate();
             image.shapeInstance[shapeIndex]->animate();
 
 
             MatrixF emat;
             MatrixF emat;
-            getEyeBaseTransform(&emat);
+            getEyeBaseTransform(&emat, mDataBlock->mountedImagesBank);
 
 
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             mountTransform.affineInverse();
             mountTransform.affineInverse();
@@ -1318,7 +1318,7 @@ void TurretShape::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat)
          if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 )
          if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 )
          {
          {
             MatrixF emat;
             MatrixF emat;
-            getRenderEyeBaseTransform(&emat);
+            getRenderEyeBaseTransform(&emat, mDataBlock->mountedImagesBank);
 
 
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]];
             mountTransform.affineInverse();
             mountTransform.affineInverse();