diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index fc0f76998..b6ba1d96a 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -940,8 +940,6 @@ ShapeBase::ShapeBase() for (i = 0; i < MaxTriggerKeys; i++) mTrigger[i] = false; - - mWeaponCamShake = NULL; } @@ -1063,15 +1061,7 @@ void ShapeBase::onRemove() if ( isClientObject() ) { - mCubeReflector.unregisterReflector(); - - if ( mWeaponCamShake ) - { - if ( mWeaponCamShake->isAdded ) - gCamFXMgr.removeFX( mWeaponCamShake ); - - SAFE_DELETE( mWeaponCamShake ); - } + mCubeReflector.unregisterReflector(); } } @@ -3161,40 +3151,9 @@ void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream) { if ( imageData->lightType == ShapeBaseImageData::WeaponFireLight ) image.lightStart = Sim::getCurrentTime(); - - // HACK: Only works properly if you are in control - // of the one and only shapeBase object in the scene - // which fires an image that uses camera shake. - if ( imageData->shakeCamera ) - { - if ( !mWeaponCamShake ) - { - mWeaponCamShake = new CameraShake(); - mWeaponCamShake->remoteControlled = true; - } - - mWeaponCamShake->init(); - mWeaponCamShake->setFrequency( imageData->camShakeFreq ); - mWeaponCamShake->setAmplitude( imageData->camShakeAmp ); - - if ( !mWeaponCamShake->isAdded ) - { - gCamFXMgr.addFX( mWeaponCamShake ); - mWeaponCamShake->isAdded = true; - } - } } updateImageState(i,0); - - if ( !image.triggerDown && !image.altTriggerDown ) - { - if ( mWeaponCamShake && mWeaponCamShake->isAdded ) - { - gCamFXMgr.removeFX( mWeaponCamShake ); - mWeaponCamShake->isAdded = false; - } - } } else { diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index a46a41580..55499a8e7 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -324,7 +324,10 @@ struct ShapeBaseImageData: public GameBaseData { /// @{ bool shakeCamera; VectorF camShakeFreq; - VectorF camShakeAmp; + VectorF camShakeAmp; + F32 camShakeDuration; + F32 camShakeRadius; + F32 camShakeFalloff; /// @} /// Maximum number of sounds this image can play at a time. @@ -903,9 +906,6 @@ protected: bool mFlipFadeVal; - /// Camera shake caused by weapon fire. - CameraShake *mWeaponCamShake; - public: /// @name Collision Notification @@ -1101,6 +1101,7 @@ protected: virtual void onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset=false); virtual void onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt); virtual void ejectShellCasing( U32 imageSlot ); + virtual void shakeCamera( U32 imageSlot ); virtual void updateDamageLevel(); virtual void updateDamageState(); virtual void onImpact(SceneObject* obj, VectorF vec); diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp index 548984ca8..e9859e3de 100644 --- a/Engine/source/T3D/shapeImage.cpp +++ b/Engine/source/T3D/shapeImage.cpp @@ -44,6 +44,7 @@ #include "sfx/sfxTypes.h" #include "scene/sceneManager.h" #include "core/stream/fileStream.h" +#include "T3D/fx/cameraFXMgr.h" //---------------------------------------------------------------------------- @@ -297,6 +298,9 @@ ShapeBaseImageData::ShapeBaseImageData() shakeCamera = false; camShakeFreq = Point3F::Zero; camShakeAmp = Point3F::Zero; + camShakeDuration = 1.5f; + camShakeRadius = 3.0f; + camShakeFalloff = 10.0f; } ShapeBaseImageData::~ShapeBaseImageData() @@ -739,10 +743,7 @@ void ShapeBaseImageData::initPersistFields() "@see lightType"); addField( "shakeCamera", TypeBool, Offset(shakeCamera, ShapeBaseImageData), - "@brief Flag indicating whether the camera should shake when this Image fires.\n\n" - "@note Camera shake only works properly if the player is in control of " - "the one and only shapeBase object in the scene which fires an Image that " - "uses camera shake." ); + "@brief Flag indicating whether the camera should shake when this Image fires.\n\n" ); addField( "camShakeFreq", TypePoint3F, Offset(camShakeFreq, ShapeBaseImageData), "@brief Frequency of the camera shaking effect.\n\n" @@ -752,6 +753,16 @@ void ShapeBaseImageData::initPersistFields() "@brief Amplitude of the camera shaking effect.\n\n" "@see shakeCamera" ); + addField( "camShakeDuration", TypeF32, Offset(camShakeDuration, ShapeBaseImageData), + "Duration (in seconds) to shake the camera." ); + + addField( "camShakeRadius", TypeF32, Offset(camShakeRadius, ShapeBaseImageData), + "Radial distance that a camera's position must be within relative to the " + "center of the explosion to be shaken." ); + + addField( "camShakeFalloff", TypeF32, Offset(camShakeFalloff, ShapeBaseImageData), + "Falloff value for the camera shake." ); + addField( "casing", TYPEID< DebrisData >(), Offset(casing, ShapeBaseImageData), "@brief DebrisData datablock to use for ejected casings.\n\n" "@see stateEjectShell" ); @@ -1028,6 +1039,9 @@ void ShapeBaseImageData::packData(BitStream* stream) { mathWrite( *stream, camShakeFreq ); mathWrite( *stream, camShakeAmp ); + stream->write( camShakeDuration ); + stream->write( camShakeRadius ); + stream->write( camShakeFalloff ); } mathWrite( *stream, shellExitDir ); @@ -1208,7 +1222,10 @@ void ShapeBaseImageData::unpackData(BitStream* stream) if ( shakeCamera ) { mathRead( *stream, &camShakeFreq ); - mathRead( *stream, &camShakeAmp ); + mathRead( *stream, &camShakeAmp ); + stream->read( &camShakeDuration ); + stream->read( &camShakeRadius ); + stream->read( &camShakeFalloff ); } mathRead( *stream, &shellExitDir ); @@ -2596,6 +2613,10 @@ void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) ejectShellCasing( imageSlot ); } + // Shake camera on client. + if (isGhost() && nextStateData.fire && image.dataBlock->shakeCamera) { + shakeCamera( imageSlot ); + } // Server must animate the shape if it is a firestate... if (isServerObject() && (image.dataBlock->state[newState].fire || image.dataBlock->state[newState].altFire)) @@ -3342,3 +3363,57 @@ void ShapeBase::ejectShellCasing( U32 imageSlot ) casing->init( shellPos, shellVel ); } + +void ShapeBase::shakeCamera( U32 imageSlot ) +{ + MountedImage& image = mMountedImageList[imageSlot]; + ShapeBaseImageData* imageData = image.dataBlock; + + if (!imageData->shakeCamera) + return; + + // Warning: this logic was duplicated from Explosion. + + // first check if explosion is near camera + GameConnection* connection = GameConnection::getConnectionToServer(); + ShapeBase *obj = dynamic_cast(connection->getControlObject()); + + bool applyShake = true; + + if (obj) + { + ShapeBase* cObj = obj; + while ((cObj = cObj->getControlObject()) != 0) + { + if (cObj->useObjsEyePoint()) + { + applyShake = false; + break; + } + } + } + + if (applyShake && obj) + { + VectorF diff; + getMuzzlePoint(imageSlot, &diff); + diff = obj->getPosition() - diff; + F32 dist = diff.len(); + if (dist < imageData->camShakeRadius) + { + CameraShake *camShake = new CameraShake; + camShake->setDuration(imageData->camShakeDuration); + camShake->setFrequency(imageData->camShakeFreq); + + F32 falloff = dist / imageData->camShakeRadius; + falloff = 1.0f + falloff * 10.0f; + falloff = 1.0f / (falloff * falloff); + + VectorF shakeAmp = imageData->camShakeAmp * falloff; + camShake->setAmplitude(shakeAmp); + camShake->setFalloff(imageData->camShakeFalloff); + camShake->init(); + gCamFXMgr.addFX(camShake); + } + } +} \ No newline at end of file diff --git a/Templates/Full/game/art/datablocks/weapons/Ryder.cs b/Templates/Full/game/art/datablocks/weapons/Ryder.cs index a94bfa8d6..7e0436aa0 100644 --- a/Templates/Full/game/art/datablocks/weapons/Ryder.cs +++ b/Templates/Full/game/art/datablocks/weapons/Ryder.cs @@ -192,9 +192,9 @@ datablock ShapeBaseImageData(RyderWeaponImage) lightBrightness = 2; // Shake camera while firing. - shakeCamera = false; - camShakeFreq = "0 0 0"; - camShakeAmp = "0 0 0"; + shakeCamera = "1"; + camShakeFreq = "10 10 10"; + camShakeAmp = "5 5 5"; // Images have a state system which controls how the animations // are run, which sounds are played, script callbacks, etc. This @@ -361,4 +361,5 @@ datablock ShapeBaseImageData(RyderWeaponImage) stateSequenceTransitionOut[13] = true; stateAllowImageChange[13] = false; stateSequence[13] = "sprint"; + camShakeDuration = "0.2"; };