diff --git a/Engine/source/T3D/camera.cpp b/Engine/source/T3D/camera.cpp index 0d742b620..09d6730b4 100644 --- a/Engine/source/T3D/camera.cpp +++ b/Engine/source/T3D/camera.cpp @@ -35,6 +35,12 @@ #include "math/mathUtils.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 CameraRadius 0.05f; @@ -254,6 +260,7 @@ Camera::Camera() { mNetFlags.clear(Ghostable); mTypeMask |= CameraObjectType; + mDataBlock = 0; mDelta.pos = Point3F(0.0f, 0.0f, 100.0f); mDelta.rot = Point3F(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; mMode = FlyMode; + mLastAbsoluteYaw = 0.0f; + mLastAbsolutePitch = 0.0f; + // For NewtonFlyMode mNewtonRotation = false; mAngularVelocity.set(0.0f, 0.0f, 0.0f); @@ -301,7 +311,7 @@ Camera::~Camera() bool Camera::onAdd() { - if(!Parent::onAdd()) + if(!Parent::onAdd() || !mDataBlock) return false; mObjBox.maxExtents = mObjScale; @@ -310,6 +320,31 @@ bool Camera::onAdd() resetWorldBox(); addToScene(); + + scriptOnAdd(); + + return true; +} + +//---------------------------------------------------------------------------- + +void Camera::onRemove() +{ + scriptOnRemove(); + removeFromScene(); + Parent::onRemove(); +} + +//---------------------------------------------------------------------------- + +bool Camera::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast(dptr); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + scriptOnNewDataBlock(); + return true; } @@ -329,14 +364,6 @@ void Camera::onEditorDisable() //---------------------------------------------------------------------------- -void Camera::onRemove() -{ - removeFromScene(); - Parent::onRemove(); -} - -//---------------------------------------------------------------------------- - // check if the object needs to be observed through its own camera... void Camera::getCameraTransform(F32* pos, MatrixF* mat) { @@ -460,79 +487,154 @@ void Camera::processTick(const Move* move) 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(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((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 if(virtualMode == TrackObjectMode && bool(mOrbitObject)) + { + // 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((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; + } + + // 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); + + 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; + } + + // Accelerate + mAngularVelocity += acc; + + // Drag + mAngularVelocity -= mAngularVelocity * drag * TickSec; + + // Rotate + mRot += mAngularVelocity * TickSec; + clampPitchAngle(mRot.x); } else { - mOrbitObject->getWorldBox().getCenter(&objPos); + mRot.x += rotVec.x; + mRot.z += rotVec.z; + clampPitchAngle(mRot.x); } - 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; - - 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 - - F32 rotVecL = rotVec.len(); - if(rotVecL > 0) - { - acc = (rotVec * force / mMass) * TickSec; - } - - // Accelerate - mAngularVelocity += acc; - - // Drag - mAngularVelocity -= mAngularVelocity * drag * TickSec; - - // Rotate - mRot += mAngularVelocity * TickSec; - clampPitchAngle(mRot.x); - } - else - { - mRot.x += rotVec.x; - mRot.z += rotVec.z; - clampPitchAngle(mRot.x); } // Update position @@ -667,6 +769,13 @@ void Camera::processTick(const Move* move) mDelta.rot = mRot; mDelta.posVec = mDelta.posVec - mDelta.pos; 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) @@ -793,7 +902,21 @@ void Camera::_setPosition(const Point3F& pos, const Point3F& rot) zRot.set(EulerF(0.0f, 0.0f, rot.z)); 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); Parent::setTransform(temp); mRot = rot; @@ -808,7 +931,21 @@ void Camera::setRotation(const Point3F& rot) zRot.set(EulerF(0.0f, 0.0f, rot.z)); 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()); Parent::setTransform(temp); mRot = rot; @@ -821,8 +958,25 @@ void Camera::_setRenderPosition(const Point3F& pos,const Point3F& rot) MatrixF xRot, zRot; xRot.set(EulerF(rot.x, 0, 0)); zRot.set(EulerF(0, 0, rot.z)); + 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); Parent::setRenderTransform(temp); } @@ -839,6 +993,11 @@ void Camera::writePacketData(GameConnection *connection, BitStream *bstream) bstream->setCompressionPoint(pos); mathWrite(*bstream, pos); 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); U32 writeMode = mMode; @@ -912,6 +1071,11 @@ void Camera::readPacketData(GameConnection *connection, BitStream *bstream) mathRead(*bstream, &pos); bstream->setCompressionPoint(pos); bstream->read(&rot.x); + if(bstream->readFlag()) + { + // Include rot.y to allow for camera banking + bstream->read(&rot.y); + } bstream->read(&rot.z); GameBase* obj = 0; @@ -1184,6 +1348,11 @@ void Camera::consoleInit() "- Fly Mode\n" "- Overhead Mode\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"); } //----------------------------------------------------------------------------- diff --git a/Engine/source/T3D/camera.h b/Engine/source/T3D/camera.h index 1d39e2875..7c0341c80 100644 --- a/Engine/source/T3D/camera.h +++ b/Engine/source/T3D/camera.h @@ -73,6 +73,9 @@ class Camera: public ShapeBase CameraLastMode = EditOrbitMode }; + /// The ExtendedMove position/rotation index used for camera movements + static S32 smExtendedMovePosRotIndex; + protected: enum MaskBits @@ -92,6 +95,8 @@ class Camera: public ShapeBase VectorF rotVec; }; + CameraData* mDataBlock; + Point3F mRot; StateDelta mDelta; @@ -106,6 +111,9 @@ class Camera: public ShapeBase Point3F mPosition; 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 /// @{ @@ -223,6 +231,7 @@ class Camera: public ShapeBase virtual bool onAdd(); virtual void onRemove(); + virtual bool onNewDataBlock( GameBaseData *dptr, bool reload ); virtual void processTick( const Move* move ); virtual void interpolateTick( F32 delta); virtual void getCameraTransform( F32* pos,MatrixF* mat ); diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp index ede671d0e..0f80ab53d 100644 --- a/Engine/source/T3D/gameBase/gameConnection.cpp +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -219,6 +219,13 @@ GameConnection::GameConnection() // first person mFirstPerson = true; mUpdateFirstPerson = false; + + // Control scheme + mUpdateControlScheme = false; + mAbsoluteRotation = false; + mAddYawToAbsRot = false; + mAddPitchToAbsRot = false; + clearDisplayDevice(); } @@ -743,7 +750,15 @@ void GameConnection::setFirstPerson(bool firstPerson) 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(mCameraSpeed); + // Control scheme + stream->write(mAbsoluteRotation); + stream->write(mAddYawToAbsRot); + stream->write(mAddPitchToAbsRot); + stream->writeString(Con::getVariable("$Client::MissionFile")); mMoveList->writeDemoStartBlock(stream); @@ -902,6 +922,11 @@ bool GameConnection::readDemoStartBlock(BitStream *stream) stream->read(&mCameraPos); stream->read(&mCameraSpeed); + // Control scheme + stream->read(&mAbsoluteRotation); + stream->read(&mAddYawToAbsRot); + stream->read(&mAddPitchToAbsRot); + char buf[256]; stream->readString(buf); Con::setVariable("$Client::MissionFile",buf); @@ -1078,6 +1103,16 @@ void GameConnection::readPacket(BitStream *bstream) else 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 if(bstream->readFlag()) { @@ -1108,6 +1143,16 @@ void GameConnection::readPacket(BitStream *bstream) if (bstream->readFlag()) 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 if(bstream->readFlag()) { @@ -1170,6 +1215,15 @@ void GameConnection::writePacket(BitStream *bstream, PacketNotify *note) } bstream->writeFlag(forceUpdate); + // Control scheme changed? + if(bstream->writeFlag(mUpdateControlScheme)) + { + bstream->writeFlag(mAbsoluteRotation); + bstream->writeFlag(mAddYawToAbsRot); + bstream->writeFlag(mAddPitchToAbsRot); + mUpdateControlScheme = false; + } + // first person changed? if(bstream->writeFlag(mUpdateFirstPerson)) { @@ -1258,6 +1312,15 @@ void GameConnection::writePacket(BitStream *bstream, PacketNotify *note) else bstream->writeFlag( false ); + // Control scheme changed? + if(bstream->writeFlag(mUpdateControlScheme)) + { + bstream->writeFlag(mAbsoluteRotation); + bstream->writeFlag(mAddYawToAbsRot); + bstream->writeFlag(mAddPitchToAbsRot); + mUpdateControlScheme = false; + } + // first person changed? if(bstream->writeFlag(mUpdateFirstPerson)) { @@ -2115,3 +2178,21 @@ DefineEngineMethod( GameConnection, setFirstPerson, void, (bool 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(); +} diff --git a/Engine/source/T3D/gameBase/gameConnection.h b/Engine/source/T3D/gameBase/gameConnection.h index 324a9c989..803fda271 100644 --- a/Engine/source/T3D/gameBase/gameConnection.h +++ b/Engine/source/T3D/gameBase/gameConnection.h @@ -91,6 +91,14 @@ private: 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: /// @name Protocol Versions @@ -270,6 +278,12 @@ public: const IDisplayDevice* getDisplayDevice() const { return mDisplayDevice; } void setDisplayDevice(IDisplayDevice* display) { mDisplayDevice = display; } 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(); diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 7133468a3..c6bb089a4 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -57,6 +57,10 @@ #include "T3D/decal/decalData.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. static F32 sAnimationTransitionTime = 0.25f; 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 sMaxPredictionTicks = 30; // Number of ticks to predict +S32 Player::smExtendedMoveHeadPosRotIndex = 0; // The ExtendedMove position/rotation index used for head movements + // Anchor point compression const F32 sAnchorMaxDistance = 32.0f; @@ -1650,6 +1656,9 @@ Player::Player() mShapeFPFlashThread[i] = 0; mShapeFPSpinThread[i] = 0; } + + mLastAbsoluteYaw = 0.0f; + mLastAbsolutePitch = 0.0f; } Player::~Player() @@ -2523,38 +2532,130 @@ void Player::updateMove(const Move* move) F32 prevZRot = mRot.z; 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(); - 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; +#ifdef TORQUE_EXTENDED_MOVE + // Work with an absolute rotation from the ExtendedMove class? + if(con && con->getControlSchemeAbsoluteRotation()) + { + doStandardMove = false; + const ExtendedMove* emove = dynamic_cast(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; + } + } +#endif + + if(doStandardMove) + { + 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; + } delta.rot = mRot; delta.rotVec.x = delta.rotVec.y = 0.0f; @@ -2566,6 +2667,13 @@ void Player::updateMove(const Move* move) delta.head = 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; zRot.set(EulerF(0.0f, 0.0f, mRot.z)); @@ -5259,7 +5367,7 @@ void Player::setTransform(const MatrixF& mat) void Player::getEyeTransform(MatrixF* mat) { - getEyeBaseTransform(mat); + getEyeBaseTransform(mat, true); // The shape instance is animated in getEyeBaseTransform() so we're // 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 // from the animation and supply our own rotation. @@ -5313,7 +5421,19 @@ void Player::getEyeBaseTransform(MatrixF* mat) else 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; @@ -5340,7 +5460,7 @@ void Player::getEyeBaseTransform(MatrixF* mat) void Player::getRenderEyeTransform(MatrixF* mat) { - getRenderEyeBaseTransform(mat); + getRenderEyeBaseTransform(mat, true); // Use the first image that is set to use the eye node for (U32 i=0; icameraCanBank) + { + // 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; @@ -5539,7 +5671,7 @@ void Player::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRend if (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1) { MatrixF nmat; - getRenderEyeBaseTransform(&nmat); + getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank); MatrixF offsetMat = image.shapeInstance[imageShapeIndex]->mNodeTransforms[data.eyeMountNode[imageShapeIndex]]; offsetMat.affineInverse(); world.mul(nmat,offsetMat); @@ -5547,7 +5679,7 @@ void Player::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRend else { MatrixF nmat; - getRenderEyeBaseTransform(&nmat); + getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank); world.mul(nmat,data.eyeOffset); } @@ -5866,6 +5998,11 @@ void Player::writePacketData(GameConnection *connection, BitStream *stream) } } 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(mRot.z); @@ -5928,6 +6065,11 @@ void Player::readPacketData(GameConnection *connection, BitStream *stream) else pos = delta.pos; 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(&rot.z); rot.x = rot.y = 0; @@ -6587,6 +6729,11 @@ void Player::consoleInit() Con::addVariable("$player::vehicleDismountTrigger", TypeS32, &sVehicleDismountTrigger, "@brief The move trigger index used to dismount player.\n\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"); } //-------------------------------------------------------------------------- diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index 86ced6b98..bdb5a2c0e 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -385,6 +385,9 @@ public: NumPoseBits = 3 }; + /// The ExtendedMove position/rotation index used for head movements + static S32 smExtendedMoveHeadPosRotIndex; + protected: /// Bit masks for different types of events @@ -444,6 +447,9 @@ protected: 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 ///< their mount, or another animation, comes through (or 13 seconds elapses). @@ -687,9 +693,9 @@ public: void setTransform(const MatrixF &mat); void getEyeTransform(MatrixF* mat); - void getEyeBaseTransform(MatrixF* mat); + void getEyeBaseTransform(MatrixF* mat, bool includeBank); 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 getMuzzleTransform(U32 imageSlot,MatrixF* mat); void getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat); diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index a68ed764d..350809286 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -171,6 +171,8 @@ ShapeBaseData::ShapeBaseData() cameraDefaultFov( 75.0f ), cameraMinFov( 5.0f ), cameraMaxFov( 120.f ), + cameraCanBank( false ), + mountedImagesBank( false ), isInvincible( false ), renderWhenDestroyed( true ), debris( NULL ), @@ -544,6 +546,10 @@ void ShapeBaseData::initPersistFields() "The minimum camera vertical FOV allowed in degrees." ); addField( "cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData), "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), "Flag controlling whether the view from this object is first person " "only." ); @@ -689,6 +695,8 @@ void ShapeBaseData::packData(BitStream* stream) stream->write(cameraMinFov); if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov)) stream->write(cameraMaxFov); + stream->writeFlag(cameraCanBank); + stream->writeFlag(mountedImagesBank); stream->writeString( debrisShapeName ); stream->writeFlag(observeThroughObject); @@ -788,6 +796,9 @@ void ShapeBaseData::unpackData(BitStream* stream) else cameraMaxFov = gShapeBaseDataProto.cameraMaxFov; + cameraCanBank = stream->readFlag(); + mountedImagesBank = stream->readFlag(); + debrisShapeName = stream->readSTString(); observeThroughObject = stream->readFlag(); @@ -1872,10 +1883,10 @@ Point3F ShapeBase::getAIRepairPoint() 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 S32 eyeNode = mDataBlock->eyeNode; @@ -1887,10 +1898,10 @@ void ShapeBase::getEyeBaseTransform(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 S32 eyeNode = mDataBlock->eyeNode; diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index af1e0287d..3ad3cc291 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -578,6 +578,12 @@ public: 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 /// @{ @@ -1618,7 +1624,7 @@ public: /// Returns the eye transform of this shape without including mounted images, IE the eyes of a player /// @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. /// @@ -1671,7 +1677,7 @@ public: virtual void getRenderMuzzleVector(U32 imageSlot,VectorF* vec); virtual void getRenderMuzzlePoint(U32 imageSlot,Point3F* pos); virtual void getRenderEyeTransform(MatrixF* mat); - virtual void getRenderEyeBaseTransform(MatrixF* mat); + virtual void getRenderEyeBaseTransform(MatrixF* mat, bool includeBank); /// @} diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp index 6784977d1..ea0ba3eba 100644 --- a/Engine/source/T3D/shapeImage.cpp +++ b/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. image.shapeInstance[shapeIndex]->animate(); - getEyeBaseTransform(&nmat); + getEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank); 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(); MatrixF emat; - getEyeBaseTransform(&emat); + getEyeBaseTransform(&emat, mDataBlock->mountedImagesBank); MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; mountTransform.affineInverse(); @@ -1985,7 +1985,7 @@ void ShapeBase::getRenderImageTransform( U32 imageSlot, MatrixF* mat, bool noEye MatrixF nmat; if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) { - getRenderEyeBaseTransform(&nmat); + getRenderEyeBaseTransform(&nmat, mDataBlock->mountedImagesBank); 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 ) { MatrixF emat; - getRenderEyeBaseTransform(&emat); + getRenderEyeBaseTransform(&emat, mDataBlock->mountedImagesBank); MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; mountTransform.affineInverse(); diff --git a/Engine/source/T3D/turret/turretShape.cpp b/Engine/source/T3D/turret/turretShape.cpp index 59f20a3e1..1abab6c09 100644 --- a/Engine/source/T3D/turret/turretShape.cpp +++ b/Engine/source/T3D/turret/turretShape.cpp @@ -1270,7 +1270,7 @@ void TurretShape::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat) image.shapeInstance[shapeIndex]->animate(); MatrixF emat; - getEyeBaseTransform(&emat); + getEyeBaseTransform(&emat, mDataBlock->mountedImagesBank); MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; mountTransform.affineInverse(); @@ -1318,7 +1318,7 @@ void TurretShape::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat) if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) { MatrixF emat; - getRenderEyeBaseTransform(&emat); + getRenderEyeBaseTransform(&emat, mDataBlock->mountedImagesBank); MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; mountTransform.affineInverse();