Game Development Community

Client-side camera

by Daniel Leidal · in Torque 3D Beginner · 04/03/2013 (2:38 pm) · 2 replies

so the stock ts interface to cameras is entirely server side, which is not very on a netgame if you want the client to be able manipulate the camera frequently. On the client side, though, is a teaser where you can turn the otherwise 1st person camera into a behind the back camera. I dug deep into the spaghetti code and found where it was setting this position. If you thought the Camera class has code for this you'd be wrong, at least for non-fly mode. The entry point for this operation is actually in ShapeBase::getCameraTransform(), where it requests a pointer to the control object and then proceeds to take the data from it and build a matrix to place the client's camera.

I'll post my change to this after a warning to anyone who is using a derived class of Player for their character rather than the stock Player class. In an attempt to avoid invalid pointer references, I do a check to see if it is in fact a Player object before I call it's functions. So all you'd have to do if you did derive from Player is change the text from "Player" to "YourPlayer".

Without further ado..

void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat)
{
// Returns camera to world space transform
// Handles first person / third person camera position

if (isServerObject() && mShapeInstance)
mShapeInstance->animateNodeSubtrees(true);

if (*pos != 0)
{
GameBase* controlObj = GameConnection::getConnectionToServer()->getControlObject();

if (controlObj && strcmp(controlObj->getClassName(), "Player") == 0)
{
Player* player = (Player*)controlObj;

Point3F playerPos = player->getPosition();
F32 playerRot = player->getRotation().z;
Point3F rotCameraPos(player->getCameraAngle(), 0.0f, playerRot);

player->setCameraOffsetDist(0.0f);
player->setCameraAimForwardDist(6.0f);

Point3F cameraPos;
cameraPos.x = player->getCameraFollowDist() * mSin( rotCameraPos.x + mDegToRad( 90.0f ) ) * mCos( -1.0f * ( rotCameraPos.z + mDegToRad( 90.0f ) ) ) + playerPos.x;
cameraPos.y = player->getCameraFollowDist() * mSin( rotCameraPos.x + mDegToRad( 90.0f ) ) * mSin( -1.0f * ( rotCameraPos.z + mDegToRad( 90.0f ) ) ) + playerPos.y;
cameraPos.z = player->getCameraFollowDist() * mSin( rotCameraPos.x ) + playerPos.z;

Point3F offset;
offset.x = player->getCameraOffsetDist() * mCos( -1.0f * ( rotCameraPos.z + mDegToRad( 90.0f ) ) );
offset.y = player->getCameraOffsetDist() * mSin( -1.0f * ( rotCameraPos.z + mDegToRad( 90.0f ) ) );
offset.z = 0.0f;

Point3F aimPoint;
aimPoint.x = playerPos.x - player->getCameraAimForwardDist() * mCos( -1.0f * ( rotCameraPos.z + mDegToRad( 90.0f ) ) );
aimPoint.y = playerPos.y - player->getCameraAimForwardDist() * mSin( -1.0f * ( rotCameraPos.z + mDegToRad( 90.0f ) ) );
aimPoint.z = playerPos.z;

cameraPos.x = cameraPos.x + offset.x;
cameraPos.y = cameraPos.y + offset.y;
cameraPos.z = cameraPos.z + offset.z;

Point3F vec;
vec = aimPoint - cameraPos;
vec.normalizeSafe();
F32 yaw, pitch;
yaw = mAtan2( vec.x, vec.y );
if( yaw < 0.0f )
yaw += M_2PI_F;
pitch = -vec.z;

MatrixF xRot, zRot;
xRot.set( EulerF( pitch, 0.0f, 0.0f ) );
zRot.set( EulerF( 0.0f, 0.0f, yaw ) );

MatrixF temp;
temp.mul( zRot, xRot );
temp.setColumn( 3, cameraPos );

*mat = temp;
}
else
{
F32 min,max;
Point3F offset;
MatrixF eye,rot;
getCameraParameters(&min,&max,&offset,&rot);
getRenderEyeTransform(&eye);
mat->mul(eye,rot);

// Use the eye transform to orient the camera
VectorF vp,vec;
vp.x = vp.z = 0;
vp.y = -(max - min) * *pos;
eye.mulV(vp,&vec);

VectorF minVec;
vp.y = -min;
eye.mulV( vp, &minVec );

// Use the camera node's pos.
Point3F osp,sp;
if (mDataBlock->cameraNode != -1)
{
mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);

// Scale the camera position before applying the transform
const Point3F& scale = getScale();
osp.convolve( scale );

getRenderTransform().mulP(osp,&sp);
}
else
getRenderTransform().getColumn(3,&sp);

// Make sure we don't extend the camera into anything solid
Point3F ep = sp + minVec + vec + offset;
disableCollision();
if (isMounted())getObjectMount()->disableCollision();
RayInfo collision;
if( mContainer && mContainer->castRay(sp, ep,(0xFFFFFFFF &(WaterObjectType|GameBaseObjectType|TriggerObjectType|DefaultObjectType)),
&collision) == true)
{
F32 vecLenSq = vec.lenSquared();
F32 adj = (-mDot(vec, collision.normal) / vecLenSq) * 0.1;
F32 newPos = getMax(0.0f, collision.t - adj);
if (newPos == 0.0f)
eye.getColumn(3,&ep);
else
ep = sp + offset + (vec * newPos);
}
mat->setColumn(3,ep);
if (isMounted())
getObjectMount()->enableCollision();
enableCollision();
}
}
else
{
getRenderEyeTransform(mat);
}

// Apply Camera FX.
mat->mul( gCamFXMgr.getTrans() );
}

I tried to save as much functionality as possible, but for the most part it's just a massive override of when the Player object is the control object.

Other things that need to be added are..

void Player::writePacketData(GameConnection *connection, BitStream *stream)
{
Parent::writePacketData(connection, stream);

stream->write(mCameraFollowDist);
stream->write(mCameraAngle);
stream->write(mCameraAimForwardDist);
stream->write(mCameraOffsetDist);

and

void Player::readPacketData(GameConnection *connection, BitStream *stream)
{
Parent::readPacketData(connection, stream);

stream->read(&mCameraFollowDist);
stream->read(&mCameraAngle);
stream->read(&mCameraAimForwardDist);
stream->read(&mCameraOffsetDist);

and the last imperitive change is actually a hack. I don't know if this causes further issues or not, but it succeeds in getting rid of the jitters in observing the player in a regularly updating camera.

void Player::interpolateTick(F32 dt)
{
dt = 0.0f;
...

This hack is pretty bad, but we'll see if it hurts something else down the road or not.

#1
04/03/2013 (2:53 pm)
Camera has a Third Person mode....

Server-side integration is part of the security setup for games - all ghosts not in camera scope are dropped (unless flagged scope-always) and are assigned new ghost id's when they come back into scope.
#2
04/03/2013 (3:35 pm)
argggg you're right. it doesn't work at all on the client.. bummed