📄 vehicle.cc
字号:
mDataBlock = 0;
mTypeMask |= VehicleObjectType;
mDelta.pos = Point3F(0,0,0);
mDelta.posVec = Point3F(0,0,0);
mDelta.warpTicks = mDelta.warpCount = 0;
mDelta.dt = 1;
mDelta.move = NullMove;
mPredictionCount = 0;
mDelta.cameraOffset.set(0,0,0);
mDelta.cameraVec.set(0,0,0);
mDelta.cameraRot.set(0,0,0);
mDelta.cameraRotVec.set(0,0,0);
mRigid.linPosition.set(0, 0, 0);
mRigid.linVelocity.set(0, 0, 0);
mRigid.angPosition.identity();
mRigid.angVelocity.set(0, 0, 0);
mRigid.linMomentum.set(0, 0, 0);
mRigid.angMomentum.set(0, 0, 0);
mContacts.count = 0;
mSteering.set(0,0);
mThrottle = 0;
mJetting = false;
mCameraOffset.set(0,0,0);
dMemset( mDustEmitterList, 0, sizeof( mDustEmitterList ) );
dMemset( mDamageEmitterList, 0, sizeof( mDamageEmitterList ) );
dMemset( mSplashEmitterList, 0, sizeof( mSplashEmitterList ) );
mDisableMove = false;
restCount = 0;
inLiquid = false;
waterWakeHandle = 0;
}
U32 Vehicle::getCollisionMask()
{
AssertFatal(false, "Vehicle::getCollisionMask is pure virtual!");
return 0;
}
Point3F Vehicle::getVelocity() const
{
return mRigid.linVelocity;
}
//----------------------------------------------------------------------------
bool Vehicle::onAdd()
{
if (!Parent::onAdd())
return false;
// When loading from a mission script, the base SceneObject's transform
// will have been set and needs to be transfered to the rigid body.
mRigid.setTransform(mObjToWorld);
// Initialize interpolation vars.
mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
mDelta.pos = mRigid.linPosition;
mDelta.posVec = Point3F(0,0,0);
// Create Emitters on the client
if( isClientObject() )
{
if( mDataBlock->dustEmitter )
{
for( int i=0; i<VehicleData::VC_NUM_DUST_EMITTERS; i++ )
{
mDustEmitterList[i] = new ParticleEmitter;
mDustEmitterList[i]->onNewDataBlock( mDataBlock->dustEmitter );
if( !mDustEmitterList[i]->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() );
delete mDustEmitterList[i];
mDustEmitterList[i] = NULL;
}
}
}
U32 j;
for( j=0; j<VehicleData::VC_NUM_DAMAGE_EMITTERS; j++ )
{
if( mDataBlock->damageEmitterList[j] )
{
mDamageEmitterList[j] = new ParticleEmitter;
mDamageEmitterList[j]->onNewDataBlock( mDataBlock->damageEmitterList[j] );
if( !mDamageEmitterList[j]->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register damage emitter for class: %s", mDataBlock->getName() );
delete mDamageEmitterList[j];
mDamageEmitterList[j] = NULL;
}
}
}
for( j=0; j<VehicleData::VC_NUM_SPLASH_EMITTERS; j++ )
{
if( mDataBlock->splashEmitterList[j] )
{
mSplashEmitterList[j] = new ParticleEmitter;
mSplashEmitterList[j]->onNewDataBlock( mDataBlock->splashEmitterList[j] );
if( !mSplashEmitterList[j]->registerObject() )
{
Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() );
delete mSplashEmitterList[j];
mSplashEmitterList[j] = NULL;
}
}
}
}
// Create a new convex.
AssertFatal(mDataBlock->collisionDetails[0] != -1, "Error, a vehicle must have a collision-1 detail!");
mConvex.mObject = this;
mConvex.pShapeBase = this;
mConvex.hullId = 0;
mConvex.box = mObjBox;
mConvex.box.min.convolve(mObjScale);
mConvex.box.max.convolve(mObjScale);
mConvex.findNodeTransform();
return true;
}
void Vehicle::onRemove()
{
U32 i=0;
for( i=0; i<VehicleData::VC_NUM_DUST_EMITTERS; i++ )
{
if( mDustEmitterList[i] )
{
mDustEmitterList[i]->deleteWhenEmpty();
mDustEmitterList[i] = NULL;
}
}
for( i=0; i<VehicleData::VC_NUM_DAMAGE_EMITTERS; i++ )
{
if( mDamageEmitterList[i] )
{
mDamageEmitterList[i]->deleteWhenEmpty();
mDamageEmitterList[i] = NULL;
}
}
for( i=0; i<VehicleData::VC_NUM_SPLASH_EMITTERS; i++ )
{
if( mSplashEmitterList[i] )
{
mSplashEmitterList[i]->deleteWhenEmpty();
mSplashEmitterList[i] = NULL;
}
}
Parent::onRemove();
}
//----------------------------------------------------------------------------
void Vehicle::processTick(const Move* move)
{
Parent::processTick(move);
// if we're not being controlled by a client, let the
// AI sub-module get a chance at producing a move
Move aiMove(NullMove);
if(isServerObject() && getAIMove(&aiMove))
move = &aiMove;
// Warp to catch up to server
if (mDelta.warpCount < mDelta.warpTicks) {
mDelta.warpCount++;
// Set new pos.
mObjToWorld.getColumn(3,&mDelta.pos);
mDelta.pos += mDelta.warpOffset;
mDelta.rot[0] = mDelta.rot[1];
mDelta.rot[1].interpolate(mDelta.warpRot[0],mDelta.warpRot[1],F32(mDelta.warpCount)/mDelta.warpTicks);
setPosition(mDelta.pos,mDelta.rot[1]);
// Pos backstepping
mDelta.posVec.x = -mDelta.warpOffset.x;
mDelta.posVec.y = -mDelta.warpOffset.y;
mDelta.posVec.z = -mDelta.warpOffset.z;
}
else {
if (!move) {
if (isGhost()) {
// If we haven't run out of prediction time,
// predict using the last known move.
if (mPredictionCount-- <= 0)
return;
move = &mDelta.move;
}
else
move = &NullMove;
}
// Process input move
updateMove(move);
// Save current rigid state interpolation
mDelta.posVec = mRigid.linPosition;
mDelta.rot[0] = mRigid.angPosition;
// Update the physics based on the integration rate
S32 count = mDataBlock->integration;
updateWorkingCollisionSet(getCollisionMask());
for (U32 i = 0; i < count; i++)
updatePos(TickSec / count);
// Wrap up interpolation info
mDelta.pos = mRigid.linPosition;
mDelta.posVec -= mRigid.linPosition;
mDelta.rot[1] = mRigid.angPosition;
// Update container database
setPosition(mRigid.linPosition, mRigid.angPosition);
setMaskBits(PositionMask);
updateContainer();
}
}
void Vehicle::interpolateTick(F32 dt)
{
Parent::interpolateTick(dt);
if(dt == 0.0f)
setRenderPosition(mDelta.pos, mDelta.rot[1]);
else
{
QuatF rot;
rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt);
Point3F pos = mDelta.pos + mDelta.posVec * dt;
setRenderPosition(pos,rot);
}
mDelta.dt = dt;
}
void Vehicle::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
updateLiftoffDust( dt );
updateDamageSmoke( dt );
updateFroth(dt);
// Update 3rd person camera offset. Camera update is done
// here as it's a client side only animation.
mCameraOffset -=
(mCameraOffset * mDataBlock->cameraDecay +
mRigid.linVelocity * mDataBlock->cameraLag) * dt;
}
//----------------------------------------------------------------------------
bool Vehicle::onNewDataBlock(GameBaseData* dptr)
{
mDataBlock = dynamic_cast<VehicleData*>(dptr);
if (!mDataBlock || !Parent::onNewDataBlock(dptr))
return false;
// Update Rigid Info
mRigid.mass = mDataBlock->mass;
mRigid.oneOverMass = 1 / mRigid.mass;
mRigid.friction = mDataBlock->body.friction;
mRigid.restitution = mDataBlock->body.restitution;
mRigid.setCenterOfMass(mDataBlock->massCenter);
// Ignores massBox, just set sphere for now. Derived objects
// can set what they want.
mRigid.setObjectInertia();
return true;
}
//----------------------------------------------------------------------------
void Vehicle::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot)
{
*min = mDataBlock->cameraMinDist;
*max = mDataBlock->cameraMaxDist;
off->set(0,0,mDataBlock->cameraOffset);
rot->identity();
}
//----------------------------------------------------------------------------
void Vehicle::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) {
getRenderEyeTransform(mat);
return;
}
// Get the shape's camera parameters.
F32 min,max;
MatrixF rot;
Point3F offset;
getCameraParameters(&min,&max,&offset,&rot);
// Start with the current eye position
MatrixF eye;
getRenderEyeTransform(&eye);
// Build a transform that points along the eye axis
// but where the Z axis is always up.
if (mDataBlock->cameraRoll)
mat->mul(eye,rot);
else {
MatrixF cam(1);
VectorF x,y,z(0,0,1);
eye.getColumn(1, &y);
mCross(y, z, &x);
x.normalize();
mCross(x, y, &z);
z.normalize();
cam.setColumn(0,x);
cam.setColumn(1,y);
cam.setColumn(2,z);
mat->mul(cam,rot);
}
// Camera is positioned straight back along the eye's -Y axis.
// A ray is cast to make sure the camera doesn't go through
// anything solid.
VectorF vp,vec;
vp.x = vp.z = 0;
vp.y = -(max - min) * *pos;
eye.mulV(vp,&vec);
// Use the camera node as the starting position if it exists.
Point3F osp,sp;
if (mDataBlock->cameraNode != -1) {
mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp);
getRenderTransform().mulP(osp,&sp);
}
else
eye.getColumn(3,&sp);
// Make sure we don't hit ourself...
disableCollision();
if (isMounted())
getObjectMount()->disableCollision();
// Cast the ray into the container database to see if we're going
// to hit anything.
RayInfo collision;
Point3F ep = sp + vec + offset + mCameraOffset;
if (mContainer->castRay(sp, ep,
~(WaterObjectType | GameBaseObjectType | DefaultObjectType),
&collision) == true) {
// Shift the collision point back a little to try and
// avoid clipping against the front camera plane.
F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1;
if (t > 0.0f)
ep = sp + offset + mCameraOffset + (vec * t);
else
eye.getColumn(3,&ep);
}
mat->setColumn(3,ep);
// Re-enable our collision.
if (isMounted())
getObjectMount()->enableCollision();
enableCollision();
}
//----------------------------------------------------------------------------
void Vehicle::getVelocity(const Point3F& r, Point3F* v)
{
mRigid.getVelocity(r, v);
}
void Vehicle::applyImpulse(const Point3F &pos, const Point3F &impulse)
{
Point3F r;
mRigid.getOriginVector(pos,&r);
mRigid.applyImpulse(r, impulse);
}
//----------------------------------------------------------------------------
void Vehicle::updateMove(const Move* move)
{
mDelta.move = *move;
// Image Triggers
if (mDamageState == Enabled) {
setImageTriggerState(0,move->trigger[0]);
setImageTriggerState(1,move->trigger[1]);
}
// Throttle
if(!mDisableMove)
mThrottle = move->y;
// Steering
if (move != &NullMove) {
F32 y = move->yaw;
mSteering.x = mClampF(mSteering.x + y,-mDataBlock->maxSteeringAngle,
mDataBlock->maxSteeringAngle);
F32 p = move->pitch;
mSteering.y = mClampF(mSteering.y + p,-mDataBlock->maxSteeringAngle,
mDataBlock->maxSteeringAngle);
}
else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -