Multiple 3rd Person Cameras
by James Laker (BurNinG) · 04/18/2007 (1:55 pm) · 10 comments
I've whipped this up in a few minutes yesterday to start testing what camera position would be the best. I then realised that I'm actually going to leave them all in for the player to select.
At the moment your Player/Vehicle/Flyingvehicle shapes (DTS) has an Eye and a Cam node. The following code will extend on this basic concept, and add more Cam nodes (Cam1...CamN). Cam should stay in the model, and now when adding more start naming them from Cam1. I put a max of 10, as this should be enough. You can change this as you see fit.
This works with TGEA and TGE.
Let's get to it.
Open up ShapeBase.h change the following:
A little further down still in ShapeBase.h:
Still in ShapeBase.h change the declaration of getCameraTransform to accept the Camera Number.
We also need to know what the last camera position was, so down further add getLastExternalCam to the other camera functions:
Now let us move on to ShapeBase.cpp...
In the the ShapeBaseData constructor (ShapebaseData::ShapeBaseData) change the following:
Also a little further down, still in the contructor make all the nodes in the array -1:
On to ShapeBase::preload. Here we load all the nodes from the Shape into the Array. Here you can also see that the first Cam Node that is missing will be the last Cam. It will ignore any cam after that.
We have to add the method to get that last cam node:
We also need to change the getCameraTransform (still in ShapeBase.cpp):
And down a little more still in ShapeBase::getCameraTransform, use we specify which Camera to use:
and add the public functions (still in GameConnection.h):
On to GameConnection.cpp, change the contructor so it always defaults to the first cam node:
Now in GameConnection::getControlCameraTransform we get the Cam based on the selected CamNo:
Again with the other Camera methods, add the new methods:
On to the Networking side of things (Yep... still in GameConnection.cpp). In GameConnection::writeDemoStartBlock add the following:
We then have to do the same in GameConnection::readDemoStartBlock:
When we make a change to the Camera we need to notify the client (GameConnection::writePacket):
And still in GameConnection:WritePacket a little further down:
Now in we also have to read those packets (GameConnection::readPacket):
And again a little down more in GameConnection::Readpacket:
And now lastly in Gameconnection.cpp add this Method right at the bottom. Here you can see the Method returning a True or false... It will return True if the last Cam Node was reached. Then we can update the Counter in the Scripting side back to Zero:
On to Camera.h we need to change the Method Declaration for getCameraTransform:
And in Camera.cpp we change the function:
If you need this for any Vehicle derived class we need to change the vehicle.h getCameraTransform declaration too:
And off course change the getCameraTransform method in vehicle.cpp:
and this (still in getCameraTransform of Vehicle):
That's it for Engine Changes... Do a Full Rebuild and pray there are no errors ;-)
Now for the scripting side. Open up starter.x\client\scripts\default.bind.cs and add the following:
An optional part is adding it to the Options->Keyboard settings. To do this open starter.x\client\scripts\optionsDlg.cs and scroll down to the part where you should this:
That should allow the player to bind a key to the Cam No Toggle in the Options Menu.
Now I hope this helps your game. Please rate the resource and if you have any questions drop em here.
Njoy!
Delete *.dso and run the game... press "c" to jump through the cameras!
At the moment your Player/Vehicle/Flyingvehicle shapes (DTS) has an Eye and a Cam node. The following code will extend on this basic concept, and add more Cam nodes (Cam1...CamN). Cam should stay in the model, and now when adding more start naming them from Cam1. I put a max of 10, as this should be enough. You can change this as you see fit.
This works with TGEA and TGE.
Let's get to it.
Open up ShapeBase.h change the following:
..
struct ShapeBaseData : public GameBaseData {
private:
typedef GameBaseData Parent;
public:
//JLAKER - MultiCamPoints
/// Various constants relating to the ShapeBaseData
enum Constants {
NumMountPoints = 32,
NumMountPointBits = 5,
MaxCollisionShapes = 8,
- AIRepairNode = 31
+ AIRepairNode = 31,
+ NumCamPoints = 16
};
//JLAKER - MultiCamPoints
bool shadowEnable;
..NumCamPoints will specify how many cameras you will allow in the game.A little further down still in ShapeBase.h:
.. S32 eyeNode; ///< Shape's eye node index + //JLAKER - MultiCamPoints - S32 cameraNode ///< Shape's camera node index + S32 cameraNode[NumCamPoints]; ///< Shape's camera node index + int camLastNode; ///< Keep track of how many nodes there were when loaded. + //JLAKER - MultiCamPoints S32 shadowNode; ..Here you can see us changing the single CameraNode and turning it into an Array.
Still in ShapeBase.h change the declaration of getCameraTransform to accept the Camera Number.
.. - virtual void getCameraTransform(F32* pos,MatrixF* mat); + //JLAKER - MultiCamPoints + virtual void getCameraTransform(F32* pos,MatrixF* mat,int camno); + //JLAKER - MultiCamPoints ..
We also need to know what the last camera position was, so down further add getLastExternalCam to the other camera functions:
.. /// Sets the FOV for this object if used as a camera virtual void setCameraFov(F32 fov); + //JLAKER - MultiCamPoints + /// Returns the Last External Camera node + S32 getLastExternalCam(); + //JLAKER - MultiCamPoints ..
Now let us move on to ShapeBase.cpp...
In the the ShapeBaseData constructor (ShapebaseData::ShapeBaseData) change the following:
.. shadowNode = -1; + //JLAKER - MultiCamPoints - cameraNode = -1; + camLastNode = 0; + //JLAKER - MultiCamPoints damageSequence = -1; ..
Also a little further down, still in the contructor make all the nodes in the array -1:
..
inheritEnergyFromMount = false;
+ //JLAKER - MultiCamPoints
+ //Default all External Camera nodes to -1
+ for (int i=0; i < NumCamPoints; i++) {
+ cameraNode[i] = -1;
+ }
+ //JLAKER - MultiCamPoints
for(U32 j = 0; j < NumHudRenderImages; j++)
..On to ShapeBase::preload. Here we load all the nodes from the Shape into the Array. Here you can also see that the first Cam Node that is missing will be the last Cam. It will ignore any cam after that.
..
+ //JLAKER - MultiCamPoints
- cameraNode = shape->findNode("cam");
- if (cameraNode == -1)
- cameraNode = eyeNode;
+ cameraNode[0] = shape->findNode("cam");
+ if (cameraNode[0] == -1)
+ cameraNode[0] = eyeNode;
+ for (i=1; i < NumCamPoints; i++) {
+ char camName[256];
+ dSprintf(camName,sizeof(camName),"cam%d",i);
+ cameraNode[i] = shape->findNode(camName);
+ //Save last camera
+ if (cameraNode[i] == -1)
+ {
+ camLastNode = i - 1;
+ break;
+ }
+ }
+ //JLAKER - MultiCamPoints
..We have to add the method to get that last cam node:
..
void ShapeBase::setCameraFov(F32 fov)
{
mCameraFov = mClampF(fov, mDataBlock->cameraMinFov, mDataBlock->cameraMaxFov);
}
+//JLAKER - multiCamPoints
+S32 ShapeBase::getLastExternalCam()
+{
+ return mDataBlock->camLastNode;
+}
+//JLAKER - multiCamPoints
..We also need to change the getCameraTransform (still in ShapeBase.cpp):
.. - void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat) + void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat,int camno) ..
And down a little more still in ShapeBase::getCameraTransform, use we specify which Camera to use:
..
// Use the camera node's pos.
Point3F osp,sp;
+ /*JLAKER - MultiCamPoints
- if (mDataBlock->cameraNode != -1) {
- mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
+ if (mDataBlock->cameraNode[camno] != -1) {
+ mShapeInstance->mNodeTransforms[mDataBlock->cameraNode[camno]].getColumn(3,&osp);
+ //JLAKER - MultiCamPoints
// Scale the camera position before applying the transform
const Point3F& scale = getScale();
..
[code]
Now we move on to the [b]GameConnection.h[/b]:
[code]
..
+ //JLAKER - MultiCamPoints
+ F32 mCamNo; ///< Decide which 3rd Person Cam to use
+ bool mUpdateCamNo; ///< Set to notify client or server of camera number change
+ //JLAKER - MultiCamPoints
bool mFirstPerson; ///< Are we currently first person or not.
bool mUpdateFirstPerson; ///< Set to notify client or server of first person change.
..and add the public functions (still in GameConnection.h):
.. void setFirstPerson(bool firstPerson); + //JLAKER - MultiCamPoints + void setCamNo(int camNo); + int getLastExternalCam(); + //JLAKER - MultiCamPoints ..
On to GameConnection.cpp, change the contructor so it always defaults to the first cam node:
// first person mFirstPerson = true; mUpdateFirstPerson = false; + //JLAKER - MultiCamPoints + mCamNo = 0; + mUpdateCamNo = false; + //JLAKER - MultiCamPoints
Now in GameConnection::getControlCameraTransform we get the Cam based on the selected CamNo:
..
if (!sChaseQueueSize || mFirstPerson || obj->onlyFirstPerson())
+ //JLAKER - MultiCamPoints
- obj->getCameraTransform(&mCameraPos,mat);
+ obj->getCameraTransform(&mCameraPos,mat,mCamNo);
+ //JLAKER - MultiCamPoints
else
{
MatrixF& hm = sChaseQueue[sChaseQueueHead];
MatrixF& tm = sChaseQueue[sChaseQueueTail];
+ //JLAKER - MultiCamPoints
+ obj->getCameraTransform(&mCameraPos,&hm,mCamNo);
- obj->getCameraTransform(&mCameraPos,&hm);
+ //JLAKER - MultiCamPoints
*mat = tm;
if (dt)
..Again with the other Camera methods, add the new methods:
..
void GameConnection::setFirstPerson(bool firstPerson)
{
mFirstPerson = firstPerson;
mUpdateFirstPerson = true;
}
+ //JLAKER - MultiCamPoints
+int GameConnection::getLastExternalCam()
+{
+ ShapeBase* obj = getCameraObject();
+ if(!obj)
+ return 0;
+
+ return obj->getLastExternalCam();
+}
+
+void GameConnection::setCamNo(int Camno)
+{
+ mCamNo = Camno;
+ mUpdateCamNo = true;
+}
+//JLAKER - MultiCamPoints
..On to the Networking side of things (Yep... still in GameConnection.cpp). In GameConnection::writeDemoStartBlock add the following:
.. stream->writeFlag(false); + //JLAKER - MultiCamPoints + stream->write(mCamNo); + //JLAKER - MultiCamPoints stream->write(mFirstPerson); stream->write(mCameraPos); stream->write(mCameraSpeed); ..
We then have to do the same in GameConnection::readDemoStartBlock:
.. + //JLAKER - MultiCamPoints + stream->read(&mCamNo); + //JLAKER - MultiCamPoints stream->read(&mFirstPerson); stream->read(&mCameraPos); stream->read(&mCameraSpeed); ..
When we make a change to the Camera we need to notify the client (GameConnection::writePacket):
..
moveWritePacket(bstream);
+ //JLAKER - MultiCamPoints
+ if(bstream->writeFlag(mUpdateCamNo))
+ {
+ bstream->writeInt(mCamNo,8);
+ mUpdateCamNo = false;
+ }
+ //JLAKER - MultiCamPoints
// first person changed?
if(bstream->writeFlag(mUpdateFirstPerson))
{
bstream->writeFlag(mFirstPerson);
mUpdateFirstPerson = false;
}
..And still in GameConnection:WritePacket a little further down:
..
else
bstream->writeFlag( false );
+ //JLAKER - MultiPointCam
+ if(bstream->writeFlag(mUpdateCamNo)) {
+ bstream->writeInt(mCamNo,8);
+ mUpdateCamNo = false;
+ }
+ //JLAKER - MultiPointCam
// first person changed?
if(bstream->writeFlag(mUpdateFirstPerson)) {
bstream->writeFlag(mFirstPerson);
mUpdateFirstPerson = false;
}
..Now in we also have to read those packets (GameConnection::readPacket):
..
}
else
setCameraObject(0);
+ //JLAKER - MultiCamPoints
+ if(bstream->readFlag())
+ {
+ setCamNo(bstream->readInt(8));
+ mUpdateCamNo = false;
+ }
+ //JLAKER - MultiCamPoints
// server changed first person
if(bstream->readFlag()) {
setFirstPerson(bstream->readFlag());
mUpdateFirstPerson = false;
}
..And again a little down more in GameConnection::Readpacket:
..
bstream->read(&mLastControlObjectChecksum);
moveReadPacket(bstream);
+ //JLAKER - MultiCamPoints
+ // client Cam No Changed
+ if(bstream->readFlag()) {
+ setCamNo(bstream->readInt(8));
+ mUpdateCamNo = false;
+ }
+ //JLAKER - MultiCamPoints
// client changed first person
if(bstream->readFlag()) {
setFirstPerson(bstream->readFlag());
mUpdateFirstPerson = false;
}
..And now lastly in Gameconnection.cpp add this Method right at the bottom. Here you can see the Method returning a True or false... It will return True if the last Cam Node was reached. Then we can update the Counter in the Scripting side back to Zero:
..
+//JLAKER - MultiCamPoints
+ConsoleMethod(GameConnection, setCamNo, bool, 3, 3, "(bool SetCamNo) Change the 3rd Person cam +(camN). It returns True when last cam node is reached.")
+{
+ //Set the camera
+ if (dAtoi(argv[2]) <= object->getLastExternalCam())
+ object->setCamNo(dAtoi(argv[2]));
+
+ //Return True if we are at last number
+ if (dAtoi(argv[2]) >= object->getLastExternalCam())
+ return true;
+ else
+ return false;
+}
+//JLAKER - MultiCamPointsOn to Camera.h we need to change the Method Declaration for getCameraTransform:
.. -void Vehicle::getCameraTransform(F32* pos,MatrixF* mat) +void Vehicle::getCameraTransform(F32* pos,MatrixF* mat,int camno) ..
And in Camera.cpp we change the function:
..
- void Camera::getCameraTransform(F32* pos, MatrixF* mat)
+ void Camera::getCameraTransform(F32* pos, MatrixF* mat,int camno)
{
// The camera doesn't support a third person mode,
// so we want to override the default ShapeBase behavior.
ShapeBase * obj = dynamic_cast<ShapeBase*>(static_cast<SimObject*>(mOrbitObject));
if(obj && static_cast<ShapeBaseData*>(obj->getDataBlock())->observeThroughObject)
- obj->getCameraTransform(pos, mat);
+ obj->getCameraTransform(pos, mat, camno);
else
..If you need this for any Vehicle derived class we need to change the vehicle.h getCameraTransform declaration too:
.. - void getCameraTransform(F32* pos, MatrixF* mat); + void getCameraTransform(F32* pos, MatrixF* mat,int camno); ..
And off course change the getCameraTransform method in vehicle.cpp:
..
+//JLAKER - MultiCamNo
-void Vehicle::getCameraTransform(F32* pos,MatrixF* mat)
+void Vehicle::getCameraTransform(F32* pos,MatrixF* mat,int camno)
{
+//JLAKER - MultiCamNo
// Returns camera to world space transform
// Handles first person / third person camera position
if (isServerObject() && mShapeInstance)
mShapeInstance->animateNodeSubtrees(true);
..and this (still in getCameraTransform of Vehicle):
..
+ //JLAKER - MultiCamPoints
- if (mDataBlock->cameraNode != -1)
- {
- mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
+ if (mDataBlock->cameraNode[camno] != -1)
+ {
+ mShapeInstance->mNodeTransforms[mDataBlock->cameraNode[camno]].getColumn(3,&osp);
+ //JLAKER - MultiCamPoints
getRenderTransform().mulP(osp,&sp);
}
else
eye.getColumn(3,&sp);
..That's it for Engine Changes... Do a Full Rebuild and pray there are no errors ;-)
Now for the scripting side. Open up starter.x\client\scripts\default.bind.cs and add the following:
..
+$CamNo = 0;
+function toggleCamNo(%val)
+{
+ if (%val)
+ {
+ echo("Changing $CamNo to " @ $CamNo);
+ $CamNo = $CamNo + 1;
+ if (ServerConnection.setCamNo($CamNo))
+ {
+ $CamNo = 0;
+ }
+ }
+}
+moveMap.bind(keyboard, c, toggleCamNo );Please make sure you dont have "c" bound to something different by default.An optional part is adding it to the Options->Keyboard settings. To do this open starter.x\client\scripts\optionsDlg.cs and scroll down to the part where you should this:
.. $RemapName[$RemapCount] = "Toggle Camera No"; $RemapCmd[$RemapCount] = "toggleCamNo"; $RemapCount++; ..
That should allow the player to bind a key to the Cam No Toggle in the Options Menu.
Now I hope this helps your game. Please rate the resource and if you have any questions drop em here.
Njoy!
Delete *.dso and run the game... press "c" to jump through the cameras!
About the author
#2
And you whipped this up in a few minutes? I'm impressed!
Though you Torquers never cease to amaze me! :-)
- Ed Johnson
04/18/2007 (3:20 pm)
Talk about useful!!! Thanks!And you whipped this up in a few minutes? I'm impressed!
Though you Torquers never cease to amaze me! :-)
- Ed Johnson
#3
04/19/2007 (1:21 am)
Hehe... took about 20min... One of those features you code and everything just works first time (touch wood).
#4
Using TGE 1.5.2 on windows w/ VS 2005 Pro I get an error in rigidshape.cc in this segment
I know that the problem is that the mDataBlock->cameraNode is not an integer but an array of S32...I'm just not sure how to figure out which index to use...so I used index 0...which should correspond to the eyeNode...it compiles w/o errors, but...
Any suggestions on the correct way to determine which index to use in this location? I suspect that mDataBlock->camLastNode might play a part here, but I really don't know.
07/07/2007 (1:19 pm)
After I fixed all my typos I still had one problem...Using TGE 1.5.2 on windows w/ VS 2005 Pro I get an error in rigidshape.cc in this segment
// Use the camera node as the starting position if it exists.
Point3F osp,sp;
//// GP - CHANGED THIS from cameraNode != -1 to an array...since it is an array.
//// MUST figure out how to determine the array index..
if (mDataBlock->cameraNode[0] != -1)
{
mShapeInstance->mNodeTransforms[mDataBlock->cameraNode[0]].getColumn(3,&osp);
getRenderTransform().mulP(osp,&sp);
}I know that the problem is that the mDataBlock->cameraNode is not an integer but an array of S32...I'm just not sure how to figure out which index to use...so I used index 0...which should correspond to the eyeNode...it compiles w/o errors, but...
Any suggestions on the correct way to determine which index to use in this location? I suspect that mDataBlock->camLastNode might play a part here, but I really don't know.
#5
What you should use though is mCamNo declared in GameConnection. I didn't create a Get Method for GameConnection, but try to use this:
Where you added this to GameConnection.h
Around line 560 in GameConnection.cpp:
Then in Rigidshape.cpp see if the Gameconnection.h is included (at the top of the file). It should look like this:
Now try using this new get(ter) method to get the current camera (node) number. If you're not able to complete it, please mail me the rigidshape.cpp/.h.
Can you tell me where my typos are, so I can update the resource?
07/09/2007 (12:39 am)
Hi, I'm not 100% ure since I dont use TGE anymore and rigidshape is not included in TGEA. What you should use though is mCamNo declared in GameConnection. I didn't create a Get Method for GameConnection, but try to use this:
Where you added this to GameConnection.h
//JLAKER - MultiCamPoints void setCamNo(int camNo); int getLastExternalCam(); //JLAKER - MultiCamPointsadd this line:
F32 GameConnection::getCamNo()
Around line 560 in GameConnection.cpp:
F32 GameConnection::getCamNo()
{
return mCamNo;
}Then in Rigidshape.cpp see if the Gameconnection.h is included (at the top of the file). It should look like this:
#include "game/gameConnection.h"
Now try using this new get(ter) method to get the current camera (node) number. If you're not able to complete it, please mail me the rigidshape.cpp/.h.
Can you tell me where my typos are, so I can update the resource?
#6
07/04/2008 (11:52 am)
Aghhhh.. don't work on TGE 1.5.2
#7
07/07/2008 (1:01 am)
@David: Where are you getting an error? Have you tried stepping through the code?
#8
i see >> void getCameraTransform(F32* pos,MatrixF* mat);
are you know it true?my engine Clean i İnstall İt New***
08/01/2008 (4:50 pm)
bro i cant see on my camera.h>> void Vehicle::getCameraTransform(F32* pos,MatrixF* mat,int camno) i see >> void getCameraTransform(F32* pos,MatrixF* mat);
are you know it true?my engine Clean i İnstall İt New***
#9
What is This?
Error 1 error C2511: 'void Camera::getCameraTransform(F32 *,MatrixF *,int)' : overloaded member function not found in 'Camera' c:\Documents and Settings\Administrator\Desktop\TGE_1_5_2_0\engine\game\camera.cc 117
08/01/2008 (5:08 pm)
in Tge.1.5.2 i see this errorWhat is This?
Error 1 error C2511: 'void Camera::getCameraTransform(F32 *,MatrixF *,int)' : overloaded member function not found in 'Camera' c:\Documents and Settings\Administrator\Desktop\TGE_1_5_2_0\engine\game\camera.cc 117
#10
08/01/2008 (5:30 pm)
@Greg i see your post that's worked Man ! Ty ;) 
Torque 3D Owner Matthew Langley
Torque