📄 vehicle.cc
字号:
mSteering.x = 0;
mSteering.y = 0;
}
// Jetting flag
if (move->trigger[3]) {
if (!mJetting && getEnergyLevel() >= mDataBlock->minJetEnergy)
mJetting = true;
if (mJetting) {
F32 newEnergy = getEnergyLevel() - mDataBlock->jetEnergyDrain;
if (newEnergy < 0) {
newEnergy = 0;
mJetting = false;
}
setEnergyLevel(newEnergy);
}
}
else
mJetting = false;
}
//----------------------------------------------------------------------------
void Vehicle::setPosition(const Point3F& pos,const QuatF& rot)
{
MatrixF mat;
rot.setMatrix(&mat);
mat.setColumn(3,pos);
Parent::setTransform(mat);
}
void Vehicle::setRenderPosition(const Point3F& pos, const QuatF& rot)
{
MatrixF mat;
rot.setMatrix(&mat);
mat.setColumn(3,pos);
Parent::setRenderTransform(mat);
}
void Vehicle::setTransform(const MatrixF& newMat)
{
mRigid.setTransform(newMat);
Parent::setTransform(newMat);
mRigid.atRest = false;
mContacts.count = 0;
}
//-----------------------------------------------------------------------------
void Vehicle::disableCollision()
{
Parent::disableCollision();
for (ShapeBase* ptr = getMountList(); ptr; ptr = ptr->getMountLink())
ptr->disableCollision();
}
void Vehicle::enableCollision()
{
Parent::enableCollision();
for (ShapeBase* ptr = getMountList(); ptr; ptr = ptr->getMountLink())
ptr->enableCollision();
}
//----------------------------------------------------------------------------
/** Update the physics
*/
void Vehicle::updatePos(F32 dt)
{
Point3F origVelocity = mRigid.linVelocity;
// Update internal forces acting on the body.
mRigid.clearForces();
updateForces(dt);
// Update collision information based on our current pos.
bool collided = false;
if (!mRigid.atRest) {
collided = updateCollision(dt);
// Now that all the forces have been processed, lets
// see if we're at rest. Basically, if the kinetic energy of
// the vehicles is less than some percentage of the energy added
// by gravity for a short period, we're considered at rest.
// This should really be part of the rigid class...
if (mCollisionList.count) {
F32 k = mRigid.getKineticEnergy();
F32 G = sVehicleGravity * dt;
F32 Kg = 0.5 * mRigid.mass * G * G;
if (k < sRestTol * Kg && ++restCount > sRestCount)
mRigid.setAtRest();
}
else
restCount = 0;
}
// Integrate forward
if (!mRigid.atRest)
mRigid.integrate(dt);
#ifndef TGE_RPGCLIENT2 /// TGE_RPGIsServerObject 未处理...
#endif
// Deal with client and server scripting, sounds, etc.
if (isServerObject()) {
// Check triggers and other objects that we normally don't
// collide with. This function must be called before notifyCollision
// as it will queue collision.
checkTriggers();
// Invoke the onCollision notify callback for all the objects
// we've just hit.
notifyCollision();
// Server side impact script callback
if (collided) {
VectorF collVec = mRigid.linVelocity - origVelocity;
F32 collSpeed = collVec.len();
if (collSpeed > mDataBlock->minImpactSpeed)
onImpact(collVec);
}
// Water script callbacks
if (!inLiquid && mWaterCoverage != 0.0f) {
Con::executef(mDataBlock,4,"onEnterLiquid",scriptThis(), Con::getFloatArg(mWaterCoverage), Con::getIntArg(mLiquidType));
inLiquid = true;
}
else if (inLiquid && mWaterCoverage == 0.0f) {
Con::executef(mDataBlock,3,"onLeaveLiquid",scriptThis(), Con::getIntArg(mLiquidType));
inLiquid = false;
}
}
else {
// Play impact sounds on the client.
if (collided) {
F32 collSpeed = (mRigid.linVelocity - origVelocity).len();
S32 impactSound = -1;
if (collSpeed >= mDataBlock->hardImpactSpeed)
impactSound = VehicleData::Body::HardImpactSound;
else
if (collSpeed >= mDataBlock->softImpactSpeed)
impactSound = VehicleData::Body::SoftImpactSound;
if (impactSound != -1 && mDataBlock->body.sound[impactSound] != NULL)
alxPlay(mDataBlock->body.sound[impactSound], &getTransform());
}
// Water volume sounds
F32 vSpeed = getVelocity().len();
if (!inLiquid && mWaterCoverage >= 0.8f) {
if (vSpeed >= mDataBlock->hardSplashSoundVel)
alxPlay(mDataBlock->waterSound[VehicleData::ImpactHard], &getTransform());
else
if (vSpeed >= mDataBlock->medSplashSoundVel)
alxPlay(mDataBlock->waterSound[VehicleData::ImpactMedium], &getTransform());
else
if (vSpeed >= mDataBlock->softSplashSoundVel)
alxPlay(mDataBlock->waterSound[VehicleData::ImpactSoft], &getTransform());
inLiquid = true;
}
else
if(inLiquid && mWaterCoverage < 0.8f) {
if (vSpeed >= mDataBlock->exitSplashSoundVel)
alxPlay(mDataBlock->waterSound[VehicleData::ExitWater], &getTransform());
inLiquid = false;
}
}
}
//----------------------------------------------------------------------------
void Vehicle::updateForces(F32 /*dt*/)
{
// Nothing here.
}
//-----------------------------------------------------------------------------
/** Update collision information
Update the convex state and check for collisions. If the object is in
collision, impact and contact forces are generated.
*/
bool Vehicle::updateCollision(F32 dt)
{
// Update collision information
MatrixF mat,cmat;
mConvex.transform = &mat;
mRigid.getTransform(&mat);
cmat = mConvex.getTransform();
mCollisionList.count = 0;
CollisionState *state = mConvex.findClosestState(cmat, getScale(), mDataBlock->collisionTol);
if (state && state->dist <= mDataBlock->collisionTol) {
//resolveDisplacement(ns,state,dt);
mConvex.getCollisionInfo(cmat, getScale(), &mCollisionList, mDataBlock->collisionTol);
}
// Resolve collisions
bool collided = resolveCollision(mRigid,mCollisionList);
resolveContacts(mRigid,mCollisionList,dt);
return collided;
}
//----------------------------------------------------------------------------
/** Resolve collision impacts
Handle collision impacts, as opposed to contacts. Impulses are calculated based
on standard collision resolution formulas.
*/
bool Vehicle::resolveCollision(Rigid& ns,CollisionList& cList)
{
// Apply impulses to resolve collision
bool colliding, collided = false;
do {
colliding = false;
for (S32 i = 0; i < cList.count; i++) {
Collision& c = cList.collision[i];
if (c.distance < mDataBlock->collisionTol) {
// Velocity into surface
Point3F v,r;
ns.getOriginVector(c.point,&r);
ns.getVelocity(r,&v);
F32 vn = mDot(v,c.normal);
// Only interested in velocities greater than sContactTol,
// velocities less than that will be dealt with as contacts
// "constraints".
if (vn < -mDataBlock->contactTol) {
// Apply impulses to the rigid body to keep it from
// penetrating the surface.
ns.resolveCollision(cList.collision[i].point,
cList.collision[i].normal);
colliding = collided = true;
// Keep track of objects we collide with
#ifdef TGE_RPGCLIENT2 /// TGE_RPGIsClientObject
if (c.object->getTypeMask() & ShapeBaseObjectType)
#else
if (!isGhost() && c.object->getTypeMask() & ShapeBaseObjectType)
#endif
{
ShapeBase* col = static_cast<ShapeBase*>(c.object);
queueCollision(col,v - col->getVelocity());
}
}
}
}
} while (colliding);
return collided;
}
//----------------------------------------------------------------------------
/** Resolve contact forces
Resolve contact forces using the "penalty" method. Forces are generated based
on the depth of penetration and the moment of inertia at the point of contact.
*/
bool Vehicle::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt)
{
// Use spring forces to manage contact constraints.
bool collided = false;
Point3F t,p(0,0,0),l(0,0,0);
for (S32 i = 0; i < cList.count; i++) {
Collision& c = cList.collision[i];
if (c.distance < mDataBlock->collisionTol) {
// Velocity into the surface
Point3F v,r;
ns.getOriginVector(c.point,&r);
ns.getVelocity(r,&v);
F32 vn = mDot(v,c.normal);
// Only interested in velocities less than mDataBlock->contactTol,
// velocities greater than that are dealt with as collisions.
if (mFabs(vn) < mDataBlock->contactTol) {
collided = true;
// Penetration force. This is actually a spring which
// will seperate the body from the collision surface.
F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r,c.normal));
F32 s = (mDataBlock->collisionTol - c.distance) * zi - ((vn / mDataBlock->contactTol) * zi);
Point3F f = c.normal * s;
// Friction impulse, calculated as a function of the
// amount of force it would take to stop the motion
// perpendicular to the normal.
Point3F uv = v - (c.normal * vn);
F32 ul = uv.len();
if (s > 0 && ul) {
uv /= -ul;
F32 u = ul * ns.getZeroImpulse(r,uv);
s *= mRigid.friction;
if (u > s)
u = s;
f += uv * u;
}
// Accumulate forces
p += f;
mCross(r,f,&t);
l += t;
}
}
}
// Contact constraint forces act over time...
ns.linMomentum += p * dt;
ns.angMomentum += l * dt;
ns.updateVelocity();
return true;
}
//----------------------------------------------------------------------------
bool Vehicle::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt)
{
SceneObject* obj = (state->a->getObject() == this)?
state->b->getObject(): state->a->getObject();
if (obj->isDisplacable() && ((obj->getTypeMask() & ShapeBaseObjectType) != 0))
{
// Try to displace the object by the amount we're trying to move
Point3F objNewMom = ns.linVelocity * obj->getMass() * 1.1;
Point3F objOldMom = obj->getMomentum();
Point3F objNewVel = objNewMom / obj->getMass();
Point3F myCenter;
Point3F theirCenter;
getWorldBox().getCenter(&myCenter);
obj->getWorldBox().getCenter(&theirCenter);
if (mDot(myCenter - theirCenter, objNewMom) >= 0.0f || objNewVel.len() < 0.01)
{
objNewMom = (theirCenter - myCenter);
objNewMom.normalize();
objNewMom *= 1.0f * obj->getMass();
objNewVel = objNewMom / obj->getMass();
}
obj->setMomentum(objNewMom);
if (obj->displaceObject(objNewVel * 1.1 * dt) == true)
{
// Queue collision and change in velocity
VectorF dv = (objOldMom - objNewMom) / obj->getMass();
queueCollision(static_cast<ShapeBase*>(obj), dv);
return true;
}
}
return false;
}
//----------------------------------------------------------------------------
void Vehicle::updateWorkingCollisionSet(const U32 mask)
{
// First, we need to adjust our velocity for possible acceleration. It is assumed
// that we will never accelerate more than 20 m/s for gravity, plus 30 m/s for
// jetting, and an equivalent 10 m/s for vehicle accel. We also assume that our
// working list is updated on a Tick basis, which means we only expand our box by
// the possible movement in that tick, plus some extra for caching purposes
Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale());
F32 len = (mRigid.linVelocity.len() + 50) * TickSec;
F32 l = (len * 1.1) + 0.1; // fudge factor
convexBox.min -= Point3F(l, l, l);
convexBox.max += Point3F(l, l, l);
disableCollision();
mConvex.updateWorkingList(convexBox, mask);
enableCollision();
}
//----------------------------------------------------------------------------
/** Check collisions with trigger and items
Perform a container search using the current bounding box
of the main body, wheels are not included. This method should
only be called on the server.
*/
void Vehicle::checkTriggers()
{
Box3F bbox = mConvex.getBoundingBox(getTransform(), getScale());
gServerContainer.findObjects(bbox,sTriggerMask,findCallback,this);
}
/** The callback used in by the checkTriggers() method.
The checkTriggers method uses a container search which will
invoke this callback on each obj that matches.
*/
void Vehicle::findCallback(SceneObject* obj,void *key)
{
Vehicle* vehicle = reinterpret_cast<Vehicle*>(key);
U32 objectMask = obj->getTypeMask();
// Check: triggers, corpses and items, basically the same things
// that the player class checks for
if (objectMask & TriggerObjectType) {
Trigger* pTrigger = static_cast<Trigger*>(obj);
pTrigger->potentialEnterObject(vehicle);
}
else if (objectMask & CorpseObjectType) {
ShapeBase* col = static_cast<ShapeBase*>(obj);
vehicle->queueCollision(col,vehicle->getVelocity() - col->getVelocity());
}
else if (objectMask & ItemObjectType) {
Item* item = static_cast<Item*>(obj);
if (vehicle != item->getCollisionObject())
vehicle->queueCollision(item,vehicle->getVelocity() - item->getVelocity());
}
}
//----------------------------------------------------------------------------
void Vehicle::writePacketData(GameConnection *connection, BitStream *stream)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -