afxCamera.cpp

Engine/source/afx/afxCamera.cpp

More...

Public Defines

define
CameraRadius() 0.05f;
define
MaxPitch() 1.3962f

Public Functions

ConsoleDocClass(afxCamera , "@brief A 3rd person camera <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMisc\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )
ConsoleDocClass(afxCameraData , "@brief A datablock that describes an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxCamera.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMisc\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )
DefineEngineMethod(afxCamera , getMode , const char * , () , "" )
DefineEngineMethod(afxCamera , getPosition , Point3F , () , "@brief Get the position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">camera.\n\n</a>" "@returns The position of the camera." )
DefineEngineMethod(afxCamera , getThirdPersonAngle , F32 , () , "" )
DefineEngineMethod(afxCamera , getThirdPersonCOIOffset , Point3F , () , "" )
DefineEngineMethod(afxCamera , getThirdPersonDistance , F32 , () , "" )
DefineEngineMethod(afxCamera , getThirdPersonOffset , Point3F , () , "" )
DefineEngineMethod(afxCamera , setCameraSubject , bool , (SceneObject *subject) , "" )
DefineEngineMethod(afxCamera , setFlyMode , void , () , "@brief Set the camera <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> fly freely." )
DefineEngineMethod(afxCamera , setThirdPersonAngle , bool , (F32 distance) , "" )
DefineEngineMethod(afxCamera , setThirdPersonDistance , bool , (F32 distance) , "" )
DefineEngineMethod(afxCamera , setThirdPersonMode , void , () , "" )
DefineEngineMethod(afxCamera , setThirdPersonOffset , void , (Point3F offset, Point3F coi_offset) , (Point3F::Max) , "" )
DefineEngineMethod(afxCamera , setThirdPersonSnap , void , () , "" )
DefineEngineStringlyVariadicMethod(afxCamera , setOrbitMode , void , 7 , 8 , "(GameBase orbitObject, <a href="/coding/class/classtransformf/">TransformF</a> mat, float minDistance, float maxDistance, float curDistance, bool ownClientObject)" "Set the camera <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> orbit around some given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param orbitObject <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> we want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">orbit.\n</a>" "@param mat A set of fields: posX posY posZ aaX aaY aaZ <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">aaTheta\n</a>" "@param minDistance Minimum distance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param maxDistance Maximum distance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param curDistance Distance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set initially from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param ownClientObj Are we observing an object owned by us?" )

Detailed Description

Public Defines

CameraRadius() 0.05f;
MaxPitch() 1.3962f

Public Functions

ConsoleDocClass(afxCamera , "@brief A 3rd person camera <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMisc\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" )

ConsoleDocClass(afxCameraData , "@brief A datablock that describes an <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxCamera.\n\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">afxMisc\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">AFX\n</a>" "@ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Datablocks\n</a>" )

DefineEngineMethod(afxCamera , getMode , const char * , () , "" )

DefineEngineMethod(afxCamera , getPosition , Point3F , () , "@brief Get the position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">camera.\n\n</a>" "@returns The position of the camera." )

DefineEngineMethod(afxCamera , getThirdPersonAngle , F32 , () , "" )

DefineEngineMethod(afxCamera , getThirdPersonCOIOffset , Point3F , () , "" )

DefineEngineMethod(afxCamera , getThirdPersonDistance , F32 , () , "" )

DefineEngineMethod(afxCamera , getThirdPersonOffset , Point3F , () , "" )

DefineEngineMethod(afxCamera , setCameraSubject , bool , (SceneObject *subject) , "" )

DefineEngineMethod(afxCamera , setFlyMode , void , () , "@brief Set the camera <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be able <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> fly freely." )

DefineEngineMethod(afxCamera , setThirdPersonAngle , bool , (F32 distance) , "" )

DefineEngineMethod(afxCamera , setThirdPersonDistance , bool , (F32 distance) , "" )

DefineEngineMethod(afxCamera , setThirdPersonMode , void , () , "" )

DefineEngineMethod(afxCamera , setThirdPersonOffset , void , (Point3F offset, Point3F coi_offset) , (Point3F::Max) , "" )

DefineEngineMethod(afxCamera , setThirdPersonSnap , void , () , "" )

DefineEngineStringlyVariadicMethod(afxCamera , setOrbitMode , void , 7 , 8 , "(GameBase orbitObject, <a href="/coding/class/classtransformf/">TransformF</a> mat, float minDistance, float maxDistance, float curDistance, bool ownClientObject)" "Set the camera <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> orbit around some given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@param orbitObject <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> we want <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">orbit.\n</a>" "@param mat A set of fields: posX posY posZ aaX aaY aaZ <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">aaTheta\n</a>" "@param minDistance Minimum distance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param maxDistance Maximum distance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> keep from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param curDistance Distance <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set initially from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param ownClientObj Are we observing an object owned by us?" )

IMPLEMENT_CO_DATABLOCK_V1(afxCameraData )

IMPLEMENT_CO_NETOBJECT_V1(afxCamera )

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//
  28// afxCamera implements a modified camera for demonstrating a third person camera style
  29// which is more common to RPG games than the standard FPS style camera. For the most part,
  30// it is a hybrid of the standard TGE camera and the third person mode of the Advanced Camera
  31// resource, authored by Thomas "Man of Ice" Lund. This camera implements the bare minimum
  32// required for demonstrating an RPG style camera and leaves tons of room for improvement. 
  33// It should be replaced with a better camera if possible.
  34//
  35// Advanced Camera Resource by Thomas "Man of Ice" Lund:
  36//   http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=5471
  37//
  38//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  39
  40#include "afx/arcaneFX.h"
  41
  42#include "math/mathUtils.h"
  43#include "math/mathIO.h"
  44#include "T3D/gameBase/gameConnection.h"
  45#include "T3D/camera.h"
  46#include "T3D/player.h"
  47#include "T3D/sfx/sfx3DWorld.h"
  48
  49#include "afx/afxCamera.h"
  50
  51#define MaxPitch      1.3962f
  52#define CameraRadius  0.05f;
  53
  54//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  55// afxCameraData
  56
  57IMPLEMENT_CO_DATABLOCK_V1(afxCameraData);
  58
  59ConsoleDocClass( afxCameraData,
  60   "@brief A datablock that describes an afxCamera.\n\n"
  61
  62   "@ingroup afxMisc\n"
  63   "@ingroup AFX\n"
  64   "@ingroup Datablocks\n"
  65);
  66
  67U32 afxCameraData::sCameraCollisionMask = TerrainObjectType | InteriorLikeObjectType | TerrainLikeObjectType;
  68
  69void afxCameraData::initPersistFields()
  70{
  71  Con::addVariable("pref::afxCamera::collisionMask", TypeS32, &sCameraCollisionMask);
  72
  73  Parent::initPersistFields();
  74}
  75
  76void afxCameraData::packData(BitStream* stream)
  77{
  78  Parent::packData(stream);
  79}
  80
  81void afxCameraData::unpackData(BitStream* stream)
  82{
  83  Parent::unpackData(stream);
  84}
  85
  86//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  87//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  88// afxCamera
  89
  90IMPLEMENT_CO_NETOBJECT_V1(afxCamera);
  91
  92ConsoleDocClass( afxCamera,
  93   "@brief A 3rd person camera object.\n\n"
  94
  95   "@ingroup afxMisc\n"
  96   "@ingroup AFX\n"
  97);
  98
  99afxCamera::afxCamera()
 100{
 101  mNetFlags.clear(Ghostable);
 102  mTypeMask |= CameraObjectType;
 103  mDelta.pos = Point3F(0,0,100);
 104  mDelta.rot = Point3F(0,0,0);
 105  mDelta.posVec = mDelta.rotVec = VectorF(0,0,0);
 106  mObjToWorld.setColumn(3, mDelta.pos);
 107  mRot = mDelta.rot;
 108  
 109  mMinOrbitDist = 0;
 110  mMaxOrbitDist = 0;
 111  mCurOrbitDist = 0;
 112  mOrbitObject = NULL;
 113  mPosition.set(0.f, 0.f, 0.f);
 114  mObservingClientObject = false;
 115  mMode = FlyMode;
 116  
 117  mCam_subject = NULL;
 118  mCoi_offset.set(0, 0, 2);
 119  mCam_offset.set(0, 0, 0);
 120  mCam_distance = 0.0f;
 121  mCam_angle = 0.0f;
 122  mCam_dirty = false;
 123      
 124  mFlymode_saved = false;
 125  mThird_person_snap_s = 1;
 126  mThird_person_snap_c = 1;
 127  mFlymode_saved_pos.zero();
 128
 129  mDamageState = Disabled;
 130}
 131
 132afxCamera::~afxCamera()
 133{
 134}
 135
 136//----------------------------------------------------------------------------
 137
 138void afxCamera::cam_update(F32 dt, bool on_server) 
 139{
 140  if (mMode == ThirdPersonMode && mCam_subject)
 141    cam_update_3pov(dt, on_server);
 142}
 143
 144void afxCamera::set_cam_pos(const Point3F& pos,const Point3F& rot)
 145{
 146   MatrixF xRot, zRot;
 147   xRot.set(EulerF(rot.x, 0, 0));
 148   zRot.set(EulerF(0, 0, rot.z));
 149   MatrixF temp;
 150   temp.mul(zRot, xRot);
 151   temp.setColumn(3, pos);
 152   Parent::setTransform(temp);
 153   mRot = rot;
 154}
 155
 156
 157//----------------------------------------------------------------------------
 158
 159
 160
 161//----------------------------------------------------------------------------
 162
 163Point3F &afxCamera::getPosition()
 164{
 165   static Point3F position;
 166   mObjToWorld.getColumn(3, &position);
 167   return position;
 168}
 169
 170//----------------------------------------------------------------------------
 171
 172
 173//----------------------------------------------------------------------------
 174//----------------------------------------------------------------------------
 175//    NEW Observer Code
 176//----------------------------------------------------------------------------
 177//----------------------------------------------------------------------------
 178void afxCamera::setFlyMode()
 179{
 180  mMode = FlyMode;
 181  if (mFlymode_saved)
 182    snapToPosition(mFlymode_saved_pos);
 183  
 184  if (bool(mOrbitObject)) 
 185  {
 186    clearProcessAfter();
 187    clearNotify(mOrbitObject);
 188  }
 189  mOrbitObject = NULL;
 190}
 191
 192void afxCamera::setOrbitMode(GameBase *obj, Point3F &pos, AngAxisF &rot, F32 minDist, F32 maxDist, F32 curDist, bool ownClientObject)
 193{
 194   mObservingClientObject = ownClientObject;
 195
 196   if(bool(mOrbitObject)) {
 197      clearProcessAfter();
 198      clearNotify(mOrbitObject);
 199   }
 200   mOrbitObject = obj;
 201   if(bool(mOrbitObject))
 202   {
 203      processAfter(mOrbitObject);
 204      deleteNotify(mOrbitObject);
 205      mOrbitObject->getWorldBox().getCenter(&mPosition);
 206     mMode = OrbitObjectMode;
 207   }
 208   else
 209   {
 210      mMode = OrbitPointMode;
 211      mPosition = pos;
 212   }
 213
 214   QuatF q(rot);
 215   MatrixF tempMat(true);
 216   q.setMatrix(&tempMat);
 217   Point3F dir;
 218   tempMat.getColumn(1, &dir);
 219
 220   set_cam_pos(mPosition, dir);
 221
 222   mMinOrbitDist = minDist;
 223   mMaxOrbitDist = maxDist;
 224   mCurOrbitDist = curDist;
 225}
 226
 227
 228void afxCamera::validateEyePoint(F32 pos, MatrixF *mat)
 229{
 230   if (pos != 0) {
 231      // Use the eye transform to orient the camera
 232      Point3F dir;
 233      mat->getColumn(1, &dir);
 234      pos *= mMaxOrbitDist - mMinOrbitDist;
 235      // Use the camera node's pos.
 236      Point3F startPos;
 237      Point3F endPos;
 238      mObjToWorld.getColumn(3,&startPos);
 239
 240      // Make sure we don't extend the camera into anything solid
 241      if(mOrbitObject)
 242         mOrbitObject->disableCollision();
 243      disableCollision();
 244      RayInfo collision;
 245
 246      SceneContainer* pContainer = isServerObject() ? &gServerContainer : &gClientContainer;
 247      if (!pContainer->castRay(startPos, startPos - dir * 2.5 * pos, afxCameraData::sCameraCollisionMask, &collision))
 248         endPos = startPos - dir * pos;
 249      else
 250      {
 251         float dot = mDot(dir, collision.normal);
 252         if(dot > 0.01)
 253         {
 254            float colDist = mDot(startPos - collision.point, dir) - (1 / dot) * CameraRadius;
 255            if(colDist > pos)
 256               colDist = pos;
 257            if(colDist < 0)
 258               colDist = 0;
 259            endPos = startPos - dir * colDist;
 260         }
 261         else
 262            endPos = startPos - dir * pos;
 263      }
 264      mat->setColumn(3,endPos);
 265      enableCollision();
 266      if(mOrbitObject)
 267         mOrbitObject->enableCollision();
 268   }
 269}
 270
 271//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 272
 273
 274
 275
 276
 277// Sets the position and calculates rotation
 278void afxCamera::snapToPosition(const Point3F& tPos) 
 279{
 280  MatrixF transMat;
 281
 282  if (mCam_subject)
 283  {
 284    // get the subject's transform
 285    MatrixF objToWorld = mCam_subject->getRenderTransform();
 286
 287    // transform the center-of-interest to world-space
 288    Point3F objPos;
 289    objToWorld.mulP(mCoi_offset, &objPos);
 290
 291    // find normalized direction vector looking from camera to coi
 292    VectorF dirVec = objPos - tPos;
 293    dirVec.normalize();
 294
 295    MathUtils::getAnglesFromVector(dirVec, mRot.z, mRot.x);
 296    mRot.x = 0 - mRot.x;
 297
 298    transMat = MathUtils::createOrientFromDir(dirVec);
 299  } 
 300  else
 301  {
 302    transMat.identity();
 303  }
 304
 305  transMat.setColumn(3, tPos);
 306  Parent::setTransform(transMat);
 307}
 308
 309void afxCamera::setCameraSubject(SceneObject* new_subject)
 310{
 311  // cleanup any existing chase subject
 312  if (mCam_subject)
 313  {
 314    if (dynamic_cast<GameBase*>(mCam_subject))
 315      clearProcessAfter();
 316    clearNotify(mCam_subject);
 317  }
 318  
 319  mCam_subject = new_subject;
 320  
 321  // set associations with new chase subject 
 322  if (mCam_subject)
 323  {
 324    if (dynamic_cast<GameBase*>(mCam_subject))
 325      processAfter((GameBase*)mCam_subject);
 326    deleteNotify(mCam_subject);
 327  }
 328
 329  mMode = (mCam_subject) ? ThirdPersonMode : FlyMode;
 330  setMaskBits(SubjectMask);
 331}
 332
 333void afxCamera::setThirdPersonOffset(const Point3F& offset) 
 334{
 335  // new method
 336  if (mCam_distance > 0.0f)
 337  {
 338    if (isClientObject())
 339    {
 340      GameConnection* conn = GameConnection::getConnectionToServer();
 341      if (conn)
 342      {
 343        // this auto switches to/from first person 
 344        if (conn->isFirstPerson())
 345        {
 346          if (mCam_distance >= 1.0f)
 347            conn->setFirstPerson(false);
 348        }
 349        else
 350        {
 351          if (mCam_distance < 1.0f)
 352            conn->setFirstPerson(true);
 353        }
 354      }
 355    }
 356
 357   mCam_offset = offset;
 358   mCam_dirty = true;
 359
 360    return;
 361  }
 362
 363  // old backwards-compatible method
 364  if (offset.y != mCam_offset.y && isClientObject())
 365  {
 366    GameConnection* conn = GameConnection::getConnectionToServer();
 367    if (conn)
 368    {
 369      // this auto switches to/from first person 
 370      if (conn->isFirstPerson())
 371      {
 372        if (offset.y <= -1.0f)
 373          conn->setFirstPerson(false);
 374      }
 375      else
 376      {
 377        if (offset.y > -1.0f)
 378          conn->setFirstPerson(true);
 379      }
 380    }
 381  }
 382
 383  mCam_offset = offset;
 384  mCam_dirty = true;
 385}
 386
 387void afxCamera::setThirdPersonOffset(const Point3F& offset, const Point3F& coi_offset) 
 388{
 389  mCoi_offset = coi_offset;
 390  setThirdPersonOffset(offset);
 391}
 392
 393void afxCamera::setThirdPersonDistance(F32 distance) 
 394{
 395  mCam_distance = distance;
 396  mCam_dirty = true;
 397}
 398
 399F32 afxCamera::getThirdPersonDistance() 
 400{
 401  return mCam_distance;
 402}
 403
 404void afxCamera::setThirdPersonAngle(F32 angle) 
 405{
 406  mCam_angle = angle;
 407  mCam_dirty = true;
 408}
 409
 410F32 afxCamera::getThirdPersonAngle() 
 411{
 412  return mCam_angle;
 413}
 414
 415void afxCamera::setThirdPersonMode()
 416{
 417  mMode = ThirdPersonMode;
 418  mFlymode_saved_pos = getPosition();
 419  mFlymode_saved = true;
 420  mCam_dirty = true;
 421  mThird_person_snap_s++;
 422}
 423
 424void afxCamera::setThirdPersonSnap()
 425{
 426  if (mMode == ThirdPersonMode)
 427    mThird_person_snap_s += 2;
 428}
 429
 430void afxCamera::setThirdPersonSnapClient()
 431{
 432  if (mMode == ThirdPersonMode)
 433    mThird_person_snap_c++;
 434}
 435
 436const char* afxCamera::getMode()
 437{
 438  switch (mMode)
 439  {
 440  case ThirdPersonMode:
 441    return "ThirdPerson";
 442  case FlyMode:
 443    return "Fly";
 444  case OrbitObjectMode:
 445    return "Orbit";
 446  }
 447
 448  return "Unknown";
 449}
 450
 451//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 452// Console Methods
 453
 454DefineEngineStringlyVariadicMethod(afxCamera, setOrbitMode, void, 7, 8,
 455  "(GameBase orbitObject, TransformF mat, float minDistance, float maxDistance, float curDistance, bool ownClientObject)"
 456  "Set the camera to orbit around some given object.\n\n"
 457  "@param   orbitObject  Object we want to orbit.\n"
 458  "@param   mat          A set of fields: posX posY posZ aaX aaY aaZ aaTheta\n"
 459  "@param   minDistance  Minimum distance to keep from object.\n"
 460  "@param   maxDistance  Maximum distance to keep from object.\n"
 461  "@param   curDistance  Distance to set initially from object.\n"
 462  "@param   ownClientObj Are we observing an object owned by us?")
 463{
 464  Point3F pos;
 465  AngAxisF aa;
 466  F32 minDis, maxDis, curDis;
 467  
 468  GameBase *orbitObject = NULL;
 469  if(Sim::findObject(argv[2],orbitObject) == false)
 470  {
 471    Con::warnf("Cannot orbit non-existing object.");
 472    object->setFlyMode();
 473    return;
 474  }
 475  
 476  dSscanf(argv[3],"%f %f %f %f %f %f %f",
 477    &pos.x,&pos.y,&pos.z,&aa.axis.x,&aa.axis.y,&aa.axis.z,&aa.angle);
 478  minDis = dAtof(argv[4]);
 479  maxDis = dAtof(argv[5]);
 480  curDis = dAtof(argv[6]);
 481  
 482  object->setOrbitMode(orbitObject, pos, aa, minDis, maxDis, curDis, (argc == 8) ? dAtob(argv[7]) : false);
 483}
 484
 485DefineEngineMethod(afxCamera, setFlyMode, void, (),, 
 486   "@brief Set the camera to be able to fly freely.")
 487{
 488   object->setFlyMode();
 489}
 490
 491DefineEngineMethod(afxCamera, getPosition, Point3F, (),,
 492   "@brief Get the position of the camera.\n\n"
 493   "@returns The position of the camera.")
 494{
 495   return object->getPosition();
 496}
 497
 498DefineEngineMethod(afxCamera, setCameraSubject, bool, (SceneObject* subject),, "")
 499{
 500   if (!subject)
 501   {
 502      Con::errorf("Camera subject not found.");
 503      return false;
 504   }
 505
 506   object->setCameraSubject(subject);
 507
 508   return true;
 509}
 510
 511DefineEngineMethod(afxCamera, setThirdPersonDistance, bool, (F32 distance),, "")
 512{
 513   object->setThirdPersonDistance(distance);
 514
 515   return true;
 516}
 517
 518DefineEngineMethod(afxCamera, getThirdPersonDistance, F32, (),, "")
 519{
 520   return object->getThirdPersonDistance();
 521}
 522
 523DefineEngineMethod(afxCamera, setThirdPersonAngle, bool, (F32 distance),, "")
 524{
 525   object->setThirdPersonAngle(distance);
 526
 527   return true;
 528}
 529
 530DefineEngineMethod(afxCamera, getThirdPersonAngle, F32, (),, "")
 531{
 532   return object->getThirdPersonAngle();
 533}
 534
 535DefineEngineMethod(afxCamera, setThirdPersonOffset, void, (Point3F offset, Point3F coi_offset), (Point3F::Max), "")
 536{
 537   if (coi_offset == Point3F::Max)
 538   {
 539      object->setThirdPersonOffset(offset);
 540   }
 541   else
 542   {
 543      object->setThirdPersonOffset(offset, coi_offset);
 544   }
 545}
 546
 547DefineEngineMethod(afxCamera, getThirdPersonOffset, Point3F, (),, "")
 548{
 549   return object->getThirdPersonOffset();
 550}
 551
 552DefineEngineMethod(afxCamera, getThirdPersonCOIOffset, Point3F, (),, "")
 553{
 554   return object->getThirdPersonCOIOffset();
 555}
 556
 557DefineEngineMethod(afxCamera, setThirdPersonMode, void, (),, "")
 558{
 559   object->setThirdPersonMode();
 560}
 561
 562DefineEngineMethod(afxCamera, setThirdPersonSnap, void, (),, "")
 563{
 564   object->setThirdPersonSnap();
 565}
 566
 567DefineEngineMethod(afxCamera, getMode, const char*, (),, "")
 568{
 569   return object->getMode();
 570}
 571
 572//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 573
 574// 3POV SECTION
 575
 576void afxCamera::cam_update_3pov(F32 dt, bool on_server) 
 577{
 578  Point3F   goal_pos;
 579  Point3F curr_pos = getRenderPosition();
 580  MatrixF   xfm = mCam_subject->getRenderTransform();
 581  Point3F coi = mCam_subject->getRenderPosition() + mCoi_offset;
 582
 583  // for player subjects, pitch is adjusted
 584  Player*   player_subj =  dynamic_cast<Player*>(mCam_subject);
 585  if (player_subj) 
 586  {
 587    if (mCam_distance > 0.0f)
 588    {
 589      // rotate xfm by amount of cam_angle
 590      F32   look_yaw = player_subj->getHeadRotation().z + mDegToRad(-mCam_angle);
 591      MatrixF  look_yaw_mtx(EulerF(0,0,look_yaw));
 592      xfm.mul(look_yaw_mtx);
 593
 594      // rotate xfm by amount of head pitch in player
 595      F32   head_pitch = player_subj->getHeadRotation().x;
 596      MatrixF  head_pitch_mtx(EulerF(head_pitch,0,0));
 597      xfm.mul(head_pitch_mtx);
 598
 599      VectorF  behind_vec(0, -mCam_distance, 0);
 600      xfm.mulP(behind_vec, &goal_pos);
 601      goal_pos += mCam_offset;
 602    }
 603    else // old backwards-compatible method
 604    {
 605      // rotate xfm by amount of head pitch in player
 606      F32   head_pitch = player_subj->getHeadRotation().x;
 607      MatrixF  head_pitch_mtx(EulerF(head_pitch,0,0));
 608      xfm.mul(head_pitch_mtx);
 609
 610      VectorF  behind_vec(0, mCam_offset.y, 0);
 611      xfm.mulP(behind_vec, &goal_pos);
 612      goal_pos.z += mCam_offset.z;
 613    }
 614  }
 615  // for non-player subjects, camera will follow, but pitch won't adjust.
 616  else 
 617  {
 618    xfm.mulP(mCam_offset, &goal_pos);
 619  }
 620
 621  // avoid view occlusion
 622  if (avoid_blocked_view(coi, goal_pos, goal_pos) && !on_server)
 623  {
 624    // snap to final position if path to goal is blocked
 625    if (test_blocked_line(curr_pos, goal_pos))
 626      mThird_person_snap_c++;
 627  }
 628
 629  // place camera into its final position 
 630
 631  // speed factor values
 632  //   15 -- tight
 633  //   10 -- normal
 634  //    5 -- loose
 635  //    1 -- very loose
 636  F32 speed_factor = 8.0f;
 637  F32 time_inc = 1.0f/speed_factor;
 638
 639  // snap to final position
 640  if (on_server || (mThird_person_snap_c > 0 || dt > time_inc))
 641  {
 642    snapToPosition(goal_pos);
 643    if (!on_server && mThird_person_snap_c > 0)
 644      mThird_person_snap_c--;
 645    return;
 646  }
 647  // interpolate to final position
 648  else
 649  {
 650    // interpretation: always move a proportion of the distance
 651    // from current location to destination that would cover the
 652    // entire distance in time_inc duration at constant velocity.
 653    F32 t = (dt >= time_inc) ? 1.0f : dt*speed_factor;
 654    snapToPosition(goal_pos*t + curr_pos*(1.0-t));
 655  }
 656}
 657
 658// See if the camera view is occluded by certain objects, 
 659// and move the camera closer to the subject in that case
 660bool afxCamera::avoid_blocked_view(const Point3F& startpos, const Point3F& endpos, Point3F& newpos) 
 661{ 
 662  // cast ray to check for intersection with potential blocker objects
 663  RayInfo hit_info;
 664  if (!getContainer()->castRay(startpos, endpos, afxCameraData::sCameraCollisionMask, &hit_info)) 
 665  {
 666    // no hit: just return original endpos
 667    newpos = endpos;
 668    return false;
 669  }
 670
 671   // did hit: return the hit location nudged forward slightly
 672  // to avoid seeing clipped portions of blocking object.
 673   Point3F sight_line = startpos - hit_info.point;
 674  sight_line.normalize();
 675  newpos = hit_info.point + sight_line*0.4f;
 676
 677  return true;
 678}
 679
 680bool afxCamera::test_blocked_line(const Point3F& startpos, const Point3F& endpos) 
 681{ 
 682  RayInfo hit_info;
 683  return getContainer()->castRay(startpos, endpos, afxCameraData::sCameraCollisionMask, &hit_info);
 684}
 685
 686//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
 687
 688// STD OVERRIDES SECTION
 689
 690bool afxCamera::onAdd()
 691{
 692  if(!Parent::onAdd())
 693    return false;
 694
 695  mObjBox.maxExtents = mObjScale;
 696  mObjBox.minExtents = mObjScale;
 697  mObjBox.minExtents.neg();
 698
 699  resetWorldBox();
 700
 701  addToScene();
 702
 703  return true;
 704}
 705
 706void afxCamera::onRemove()
 707{
 708  removeFromScene();
 709  Parent::onRemove();
 710}
 711
 712void afxCamera::onDeleteNotify(SimObject *obj)
 713{
 714  Parent::onDeleteNotify(obj);
 715
 716  if (obj == (SimObject*)mOrbitObject)
 717  {
 718    mOrbitObject = NULL;
 719    if (mMode == OrbitObjectMode)
 720      mMode = OrbitPointMode;
 721  }
 722
 723  if (obj == mCam_subject)
 724  {
 725    mCam_subject = NULL;
 726  }
 727}
 728
 729void afxCamera::advanceTime(F32 dt) 
 730{
 731  Parent::advanceTime(dt);
 732
 733  if (gSFX3DWorld)
 734  {
 735     if (mMode == ThirdPersonMode && mCam_subject)
 736     {
 737        if (gSFX3DWorld->getListener() != mCam_subject)
 738           gSFX3DWorld->setListener(mCam_subject);
 739     }
 740     else if (mMode == FlyMode)
 741     {
 742        if (gSFX3DWorld->getListener() != this)
 743           gSFX3DWorld->setListener(this);
 744     }
 745  }
 746
 747  cam_update(dt, false);
 748}
 749
 750void afxCamera::processTick(const Move* move)
 751{
 752  Parent::processTick(move);
 753  Point3F vec,pos;
 754
 755  // move will be NULL unless camera becomes the control object as in FlyMode
 756  if (move) 
 757  {
 758    // UPDATE ORIENTATION //
 759     mDelta.rotVec = mRot;
 760    mObjToWorld.getColumn(3, &mDelta.posVec);
 761    mRot.x = mClampF(mRot.x + move->pitch, -MaxPitch, MaxPitch);
 762    mRot.z += move->yaw;
 763
 764    // ORBIT MODE // 
 765    if (mMode == OrbitObjectMode || mMode == OrbitPointMode)
 766    {
 767      if(mMode == OrbitObjectMode && bool(mOrbitObject))
 768      {
 769        // If this is a shapebase, use its render eye transform
 770        // to avoid jittering.
 771        GameBase *castObj = mOrbitObject;
 772        ShapeBase* shape = dynamic_cast<ShapeBase*>(castObj);
 773        if( shape != NULL ) {
 774          MatrixF ret;
 775          shape->getRenderEyeTransform( &ret );
 776          mPosition = ret.getPosition();
 777        } 
 778        else 
 779        {
 780          // Hopefully this is a static object that doesn't move,
 781          // because the worldbox doesn't get updated between ticks.
 782          mOrbitObject->getWorldBox().getCenter(&mPosition);
 783        }
 784      }
 785      set_cam_pos(mPosition, mRot);
 786      validateEyePoint(1.0f, &mObjToWorld);
 787      pos = mPosition;
 788    }
 789
 790    // NON-ORBIT MODE (FLY MODE) //
 791    else // if (mode == FlyMode)
 792    {
 793      // Update pos
 794      bool faster = move->trigger[0] || move->trigger[1];
 795      F32 scale = Camera::getMovementSpeed() * (faster + 1);
 796
 797      mObjToWorld.getColumn(3,&pos);
 798      mObjToWorld.getColumn(0,&vec);
 799      pos += vec * move->x * TickSec * scale;
 800      mObjToWorld.getColumn(1,&vec);
 801      pos += vec * move->y * TickSec * scale;
 802      mObjToWorld.getColumn(2,&vec);
 803      pos += vec * move->z * TickSec * scale;
 804      set_cam_pos(pos,mRot);
 805    }
 806
 807    // If on the client, calc delta for backstepping
 808    if (isClientObject()) 
 809    {
 810      mDelta.pos = pos;
 811     mDelta.rot = mRot;
 812     mDelta.posVec = mDelta.posVec - mDelta.pos;
 813     mDelta.rotVec = mDelta.rotVec - mDelta.rot;
 814    }
 815    else
 816    {
 817      setMaskBits(MoveMask);
 818    }
 819  }
 820  else // if (!move)
 821  {
 822    if (isServerObject())
 823      cam_update(1.0/32.0, true);
 824  }
 825
 826  if (getControllingClient() && mContainer)
 827    updateContainer();
 828}
 829
 830void afxCamera::interpolateTick(F32 dt)
 831{
 832  Parent::interpolateTick(dt);
 833
 834  if (mMode == ThirdPersonMode)
 835    return;
 836
 837  Point3F rot = mDelta.rot + mDelta.rotVec * dt;
 838
 839  if(mMode == OrbitObjectMode || mMode == OrbitPointMode)
 840  {
 841    if(mMode == OrbitObjectMode && bool(mOrbitObject))
 842    {
 843      // If this is a shapebase, use its render eye transform
 844      // to avoid jittering.
 845      GameBase *castObj = mOrbitObject;
 846      ShapeBase* shape = dynamic_cast<ShapeBase*>(castObj);
 847      if( shape != NULL ) 
 848      {
 849        MatrixF ret;
 850        shape->getRenderEyeTransform( &ret );
 851        mPosition = ret.getPosition();
 852      } 
 853      else 
 854      {
 855        // Hopefully this is a static object that doesn't move,
 856        // because the worldbox doesn't get updated between ticks.
 857        mOrbitObject->getWorldBox().getCenter(&mPosition);
 858      }
 859    }
 860    set_cam_pos(mPosition, rot);
 861    validateEyePoint(1.0f, &mObjToWorld);
 862  }
 863  else 
 864  {
 865    // NOTE - posVec is 0,0,0 unless cam is control-object and process tick is
 866    // updating the delta
 867    Point3F pos = mDelta.pos + mDelta.posVec * dt;
 868    set_cam_pos(pos,rot);
 869  }
 870}
 871
 872void afxCamera::writePacketData(GameConnection *connection, BitStream *bstream)
 873{
 874  // Update client regardless of status flags.
 875  Parent::writePacketData(connection, bstream);
 876
 877  Point3F pos; mObjToWorld.getColumn(3, &pos);
 878  bstream->setCompressionPoint(pos);                                      // SET COMPRESSION POINT
 879  mathWrite(*bstream, pos);                                               // SND POS
 880  bstream->write(mRot.x);                                                 // SND X ROT
 881  bstream->write(mRot.z);                                                 // SND Z ROT
 882
 883  if (bstream->writeFlag(mCam_dirty))
 884  {
 885    mathWrite(*bstream, mCam_offset);                                        // SND CAM_OFFSET
 886    mathWrite(*bstream, mCoi_offset);                                        // SND COI_OFFSET
 887    bstream->write(mCam_distance);
 888    bstream->write(mCam_angle);
 889   mCam_dirty = false;
 890  }
 891
 892  U32 writeMode = mMode;
 893  Point3F writePos = mPosition;
 894  S32 gIndex = -1;
 895  if (mMode == OrbitObjectMode)
 896  {
 897    gIndex = bool(mOrbitObject) ? connection->getGhostIndex(mOrbitObject): -1;
 898    if(gIndex == -1)
 899    {
 900      writeMode = OrbitPointMode;
 901      mOrbitObject->getWorldBox().getCenter(&writePos);
 902    }
 903  }
 904
 905  bstream->writeRangedU32(writeMode, CameraFirstMode, CameraLastMode);    // SND MODE
 906  if (writeMode == ThirdPersonMode)
 907  {
 908    bstream->write(mThird_person_snap_s > 0);                              // SND SNAP
 909    if (mThird_person_snap_s > 0)
 910      mThird_person_snap_s--;
 911  }
 912
 913  if (writeMode == OrbitObjectMode || writeMode == OrbitPointMode)
 914  {
 915    bstream->write(mMinOrbitDist);                                        // SND ORBIT MIN DIST
 916    bstream->write(mMaxOrbitDist);                                        // SND ORBIT MAX DIST
 917    bstream->write(mCurOrbitDist);                                        // SND ORBIT CURR DIST
 918    if(writeMode == OrbitObjectMode)
 919    {
 920      bstream->writeFlag(mObservingClientObject);                         // SND OBSERVING CLIENT OBJ
 921      bstream->writeInt(gIndex, NetConnection::GhostIdBitSize);           // SND ORBIT OBJ
 922    }
 923    if (writeMode == OrbitPointMode)
 924      bstream->writeCompressedPoint(writePos);                            // WRITE COMPRESSION POINT
 925  }
 926}
 927
 928void afxCamera::readPacketData(GameConnection *connection, BitStream *bstream)
 929{
 930  Parent::readPacketData(connection, bstream);
 931
 932  Point3F pos,rot;
 933  mathRead(*bstream, &pos);                                               // RCV POS
 934  bstream->setCompressionPoint(pos);
 935  bstream->read(&rot.x);                                                  // RCV X ROT
 936  bstream->read(&rot.z);                                                  // RCV Z ROT
 937
 938  if (bstream->readFlag())
 939  {
 940    Point3F new_cam_offset, new_coi_offset;
 941    mathRead(*bstream, &new_cam_offset);                                    // RCV CAM_OFFSET
 942    mathRead(*bstream, &new_coi_offset);                                    // RCV COI_OFFSET
 943    bstream->read(&mCam_distance);
 944    bstream->read(&mCam_angle);
 945    setThirdPersonOffset(new_cam_offset, new_coi_offset);
 946  }
 947
 948  GameBase* obj = 0;
 949  mMode = bstream->readRangedU32(CameraFirstMode,                          // RCV MODE
 950    CameraLastMode);
 951  if (mMode == ThirdPersonMode)
 952  {
 953    bool snap; bstream->read(&snap);
 954    if (snap)
 955      mThird_person_snap_c++;
 956  }
 957
 958  mObservingClientObject = false;
 959  if (mMode == OrbitObjectMode || mMode == OrbitPointMode) {
 960    bstream->read(&mMinOrbitDist);
 961    bstream->read(&mMaxOrbitDist);
 962    bstream->read(&mCurOrbitDist);
 963
 964    if(mMode == OrbitObjectMode)
 965    {
 966      mObservingClientObject = bstream->readFlag();
 967      S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize);
 968      obj = static_cast<GameBase*>(connection->resolveGhost(gIndex));
 969    }
 970    if (mMode == OrbitPointMode)
 971      bstream->readCompressedPoint(&mPosition);
 972  }
 973  if (obj != (GameBase*)mOrbitObject) {
 974    if (mOrbitObject) {
 975      clearProcessAfter();
 976      clearNotify(mOrbitObject);
 977    }
 978    mOrbitObject = obj;
 979    if (mOrbitObject) {
 980      processAfter(mOrbitObject);
 981      deleteNotify(mOrbitObject);
 982    }
 983  }
 984
 985  if (mMode == ThirdPersonMode)
 986    return;
 987
 988  set_cam_pos(pos,rot);
 989  mDelta.pos = pos;
 990  mDelta.rot = rot;
 991  mDelta.rotVec.set(0,0,0);
 992  mDelta.posVec.set(0,0,0);
 993}
 994
 995U32 afxCamera::packUpdate(NetConnection* conn, U32 mask, BitStream *bstream)
 996{
 997  U32 retMask = Parent::packUpdate(conn,mask,bstream);
 998
 999  // The rest of the data is part of the control object packet update.
1000  // If we're controlled by this client, we don't need to send it.
1001  //if(bstream->writeFlag(getControllingClient() == conn && !(mask & InitialUpdateMask)))
1002  //   return 0;
1003
1004  if (bstream->writeFlag(mask & MoveMask)) {
1005    Point3F pos;
1006    mObjToWorld.getColumn(3,&pos);
1007    bstream->write(pos.x);
1008    bstream->write(pos.y);
1009    bstream->write(pos.z);
1010    bstream->write(mRot.x);
1011    bstream->write(mRot.z);
1012  }
1013
1014  if (bstream->writeFlag(mask & SubjectMask)) 
1015  {
1016    S32 ghost_id = (mCam_subject) ? conn->getGhostIndex(mCam_subject) : -1;
1017    if (bstream->writeFlag(ghost_id != -1))
1018      bstream->writeRangedU32(U32(ghost_id), 0, NetConnection::MaxGhostCount);
1019    else if (mCam_subject)
1020      retMask |= SubjectMask;
1021  }
1022
1023  return retMask;
1024}
1025
1026void afxCamera::unpackUpdate(NetConnection *conn, BitStream *bstream)
1027{
1028  Parent::unpackUpdate(conn,bstream);
1029
1030  // controlled by the client?
1031  //if(bstream->readFlag())
1032  //   return;
1033
1034  if (bstream->readFlag()) {
1035    Point3F pos,rot;
1036    bstream->read(&pos.x);
1037    bstream->read(&pos.y);
1038    bstream->read(&pos.z);
1039    bstream->read(&rot.x);
1040    bstream->read(&rot.z);
1041    set_cam_pos(pos,rot);
1042
1043    // New delta for client side interpolation
1044   mDelta.pos = pos;
1045   mDelta.rot = rot;
1046   mDelta.posVec = mDelta.rotVec = VectorF(0,0,0);
1047  }
1048
1049  if (bstream->readFlag()) 
1050  {
1051    if (bstream->readFlag())
1052    {
1053      S32 ghost_id = bstream->readRangedU32(0, NetConnection::MaxGhostCount);
1054     mCam_subject = dynamic_cast<GameBase*>(conn->resolveGhost(ghost_id));
1055    }
1056    else
1057      mCam_subject = NULL;
1058  }
1059}
1060
1061// Override to ensure both are kept in scope
1062void afxCamera::onCameraScopeQuery(NetConnection* conn, CameraScopeQuery* query) 
1063{
1064  if (mCam_subject)
1065    conn->objectInScope(mCam_subject);
1066  Parent::onCameraScopeQuery(conn, query);
1067}
1068
1069//----------------------------------------------------------------------------
1070// check if the object needs to be observed through its own camera...
1071void afxCamera::getCameraTransform(F32* pos, MatrixF* mat)
1072{
1073  // The camera doesn't support a third person mode,
1074  // so we want to override the default ShapeBase behavior.
1075  ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
1076  if (obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
1077    obj->getCameraTransform(pos, mat);
1078  else
1079    getEyeTransform(mat);
1080}
1081
1082void afxCamera::setTransform(const MatrixF& mat)
1083{
1084  // This method should never be called on the client.
1085
1086  // This currently converts all rotation in the mat into
1087  // rotations around the z and x axis.
1088  Point3F pos,vec;
1089  mat.getColumn(1,&vec);
1090  mat.getColumn(3,&pos);
1091  Point3F rot(-mAtan2(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)),0,-mAtan2(-vec.x,vec.y));
1092  set_cam_pos(pos,rot);
1093}
1094
1095void afxCamera::onEditorEnable()
1096{
1097  mNetFlags.set(Ghostable);
1098}
1099
1100void afxCamera::onEditorDisable()
1101{
1102  mNetFlags.clear(Ghostable);
1103}
1104
1105F32 afxCamera::getCameraFov()
1106{
1107  ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
1108  if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
1109    return(obj->getCameraFov());
1110  else
1111    return(Parent::getCameraFov());
1112}
1113
1114F32 afxCamera::getDefaultCameraFov()
1115{
1116  ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
1117  if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
1118    return(obj->getDefaultCameraFov());
1119  else
1120    return(Parent::getDefaultCameraFov());
1121}
1122
1123bool afxCamera::isValidCameraFov(F32 fov)
1124{
1125  ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
1126  if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
1127    return(obj->isValidCameraFov(fov));
1128  else
1129    return(Parent::isValidCameraFov(fov));
1130}
1131
1132void afxCamera::setCameraFov(F32 fov)
1133{
1134  ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
1135  if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
1136    obj->setCameraFov(fov);
1137  else
1138    Parent::setCameraFov(fov);
1139}
1140
1141F32 afxCamera::getDamageFlash() const
1142{
1143  if (mMode == OrbitObjectMode && isServerObject() && bool(mOrbitObject))
1144  {
1145    const GameBase *castObj = mOrbitObject;
1146    const ShapeBase* psb = dynamic_cast<const ShapeBase*>(castObj);
1147    if (psb)
1148      return psb->getDamageFlash();
1149  }
1150
1151  return mDamageFlash;
1152}
1153
1154F32 afxCamera::getWhiteOut() const
1155{
1156  if (mMode == OrbitObjectMode && isServerObject() && bool(mOrbitObject))
1157  {
1158    const GameBase *castObj = mOrbitObject;
1159    const ShapeBase* psb = dynamic_cast<const ShapeBase*>(castObj);
1160    if (psb)
1161      return psb->getWhiteOut();
1162  }
1163
1164  return mWhiteOut;
1165}
1166
1167//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1168
1169void afxCamera::setControllingClient( GameConnection* client )
1170{
1171   GameBase::setControllingClient( client );
1172}
1173
1174//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
1175