📄 vehicle.cc
字号:
{
Parent::writePacketData(connection, stream);
mathWrite(*stream, mSteering);
mathWrite(*stream, mRigid.linPosition);
mathWrite(*stream, mRigid.angPosition);
mathWrite(*stream, mRigid.linMomentum);
mathWrite(*stream, mRigid.angMomentum);
stream->writeFlag(mRigid.atRest);
stream->writeFlag(mContacts.count == 0);
stream->writeFlag(mDisableMove);
stream->setCompressionPoint(mRigid.linPosition);
}
void Vehicle::readPacketData(GameConnection *connection, BitStream *stream)
{
Parent::readPacketData(connection, stream);
mathRead(*stream, &mSteering);
mathRead(*stream, &mRigid.linPosition);
mathRead(*stream, &mRigid.angPosition);
mathRead(*stream, &mRigid.linMomentum);
mathRead(*stream, &mRigid.angMomentum);
mRigid.atRest = stream->readFlag();
if (stream->readFlag())
mContacts.count = 0;
// Update the rigid state based on the new information
mRigid.updateInertialTensor();
mRigid.updateVelocity();
mRigid.updateCenterOfMass();
mDisableMove = stream->readFlag();
stream->setCompressionPoint(mRigid.linPosition);
}
//----------------------------------------------------------------------------
U32 Vehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
stream->writeFlag(mJetting);
// The rest of the data is part of the control object packet update.
// If we're controlled by this client, we don't need to send it.
if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask)))
return retMask;
F32 yaw = (mSteering.x + mDataBlock->maxSteeringAngle) / (2 * mDataBlock->maxSteeringAngle);
F32 pitch = (mSteering.y + mDataBlock->maxSteeringAngle) / (2 * mDataBlock->maxSteeringAngle);
stream->writeFloat(yaw,9);
stream->writeFloat(pitch,9);
mDelta.move.pack(stream);
if (stream->writeFlag(mask & PositionMask))
{
stream->writeCompressedPoint(mRigid.linPosition);
mathWrite(*stream, mRigid.angPosition);
mathWrite(*stream, mRigid.linMomentum);
mathWrite(*stream, mRigid.angMomentum);
stream->writeFlag(mRigid.atRest);
}
// send energy only to clients which need it
bool found = false;
if (mask & EnergyMask)
{
for (ShapeBase* ptr = getMountList(); ptr; ptr = ptr->getMountLink())
{
GameConnection * controllingClient = ptr->getControllingClient();
if(controllingClient == con)
{
if(controllingClient->getControlObject() != this)
found = true;
break;
}
}
}
// write it...
if(stream->writeFlag(found))
stream->writeFloat(mClampF(getEnergyValue(), 0.f, 1.f), 8);
return retMask;
}
void Vehicle::unpackUpdate(NetConnection *con, BitStream *stream)
{
Parent::unpackUpdate(con,stream);
mJetting = stream->readFlag();
if (stream->readFlag())
return;
F32 yaw = stream->readFloat(9);
F32 pitch = stream->readFloat(9);
mSteering.x = (2 * yaw * mDataBlock->maxSteeringAngle) - mDataBlock->maxSteeringAngle;
mSteering.y = (2 * pitch * mDataBlock->maxSteeringAngle) - mDataBlock->maxSteeringAngle;
mDelta.move.unpack(stream);
if (stream->readFlag()) {
mPredictionCount = sMaxPredictionTicks;
F32 speed = mRigid.linVelocity.len();
mDelta.warpRot[0] = mRigid.angPosition;
// Read in new position and momentum values
stream->readCompressedPoint(&mRigid.linPosition);
mathRead(*stream, &mRigid.angPosition);
mathRead(*stream, &mRigid.linMomentum);
mathRead(*stream, &mRigid.angMomentum);
mRigid.atRest = stream->readFlag();
mRigid.updateVelocity();
if (isProperlyAdded()) {
// Determin number of ticks to warp based on the average
// of the client and server velocities.
Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt;
mDelta.warpOffset = mRigid.linPosition - cp;
// Calc the distance covered in one tick as the average of
// the old speed and the new speed from the server.
F32 dt,as = (speed + mRigid.linVelocity.len()) * 0.5 * TickSec;
// Cal how many ticks it will take to cover the warp offset.
// If it's less than what's left in the current tick, we'll just
// warp in the remaining time.
if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks)
dt = mDelta.dt + sMaxWarpTicks;
else
dt = (dt <= mDelta.dt)? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt;
// Adjust current frame interpolation
if (mDelta.dt) {
mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt));
mDelta.posVec = (cp - mDelta.pos) / mDelta.dt;
QuatF cr;
cr.interpolate(mDelta.rot[1],mDelta.rot[0],mDelta.dt);
mDelta.rot[1].interpolate(cr,mRigid.angPosition,mDelta.dt / dt);
mDelta.rot[0].extrapolate(mDelta.rot[1],cr,mDelta.dt);
}
// Calculated multi-tick warp
mDelta.warpCount = 0;
mDelta.warpTicks = (S32)(mFloor(dt));
if (mDelta.warpTicks) {
mDelta.warpOffset = mRigid.linPosition - mDelta.pos;
mDelta.warpOffset /= mDelta.warpTicks;
mDelta.warpRot[0] = mDelta.rot[1];
mDelta.warpRot[1] = mRigid.angPosition;
}
}
else {
// Set the vehicle to the server position
mDelta.dt = 0;
mDelta.pos = mRigid.linPosition;
mDelta.posVec.set(0,0,0);
mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition;
mDelta.warpCount = mDelta.warpTicks = 0;
setPosition(mRigid.linPosition, mRigid.angPosition);
}
mRigid.updateCenterOfMass();
}
// energy?
if(stream->readFlag())
setEnergyLevel(stream->readFloat(8) * mDataBlock->maxEnergy);
}
//----------------------------------------------------------------------------
void Vehicle::initPersistFields()
{
Parent::initPersistFields();
addField("disableMove", TypeBool, Offset(mDisableMove, Vehicle));
}
void Vehicle::mountObject(ShapeBase* obj, U32 node)
{
Parent::mountObject(obj, node);
// Clear objects off the working list that are from objects mounted to us.
// (This applies mostly to players...)
for (CollisionWorkingList* itr = mConvex.getWorkingList().wLink.mNext; itr != &mConvex.getWorkingList(); itr = itr->wLink.mNext) {
if (itr->mConvex->getObject() == obj) {
CollisionWorkingList* cl = itr;
itr = itr->wLink.mPrev;
cl->free();
}
}
}
//----------------------------------------------------------------------------
void Vehicle::updateLiftoffDust( F32 dt )
{
if( !mDustEmitterList[0] ) return;
Point3F startPos = getPosition();
Point3F endPos = startPos + Point3F( 0.0, 0.0, -mDataBlock->triggerDustHeight );
RayInfo rayInfo;
if( !getContainer()->castRay( startPos, endPos, TerrainObjectType, &rayInfo ) )
{
return;
}
TerrainBlock* tBlock = static_cast<TerrainBlock*>(rayInfo.object);
S32 mapIndex = tBlock->mMPMIndex[0];
MaterialPropertyMap* pMatMap = static_cast<MaterialPropertyMap*>(Sim::findObject("MaterialPropertyMap"));
const MaterialPropertyMap::MapEntry* pEntry = pMatMap->getMapEntryFromIndex(mapIndex);
if(pEntry)
{
S32 x;
ColorF colorList[ParticleEngine::PC_COLOR_KEYS];
for(x = 0; x < 2; ++x)
colorList[x].set( pEntry->puffColor[x].red, pEntry->puffColor[x].green, pEntry->puffColor[x].blue, pEntry->puffColor[x].alpha );
for(x = 2; x < ParticleEngine::PC_COLOR_KEYS; ++x)
colorList[x].set( 1.0, 1.0, 1.0, 0.0 );
mDustEmitterList[0]->setColors( colorList );
}
Point3F contactPoint = rayInfo.point + Point3F( 0.0, 0.0, mDataBlock->dustHeight );
mDustEmitterList[0]->emitParticles( contactPoint, contactPoint, rayInfo.normal, getVelocity(), (U32)(dt * 1000) );
}
//----------------------------------------------------------------------------
void Vehicle::updateDamageSmoke( F32 dt )
{
for( S32 j=VehicleData::VC_NUM_DAMAGE_LEVELS-1; j>=0; j-- )
{
F32 damagePercent = mDamage / mDataBlock->maxDamage;
if( damagePercent >= mDataBlock->damageLevelTolerance[j] )
{
for( int i=0; i<mDataBlock->numDmgEmitterAreas; i++ )
{
MatrixF trans = getTransform();
Point3F offset = mDataBlock->damageEmitterOffset[i];
trans.mulP( offset );
Point3F emitterPoint = offset;
if( pointInWater(offset ) )
{
U32 emitterOffset = VehicleData::VC_BUBBLE_EMITTER;
if( mDamageEmitterList[emitterOffset] )
{
mDamageEmitterList[emitterOffset]->emitParticles( emitterPoint, emitterPoint, Point3F( 0.0, 0.0, 1.0 ), getVelocity(), (U32)( dt * 1000 ) );
}
}
else
{
if( mDamageEmitterList[j] )
{
mDamageEmitterList[j]->emitParticles( emitterPoint, emitterPoint, Point3F( 0.0, 0.0, 1.0 ), getVelocity(), (U32)(dt * 1000));
}
}
}
break;
}
}
}
//--------------------------------------------------------------------------
void Vehicle::updateFroth( F32 dt )
{
// update bubbles
Point3F moveDir = getVelocity();
Point3F contactPoint;
if( !collidingWithWater( contactPoint ) )
{
if(waterWakeHandle)
{
alxStop(waterWakeHandle);
waterWakeHandle = 0;
}
return;
}
F32 speed = moveDir.len();
if( speed < mDataBlock->splashVelEpsilon ) speed = 0.0;
U32 emitRate = (U32)(speed * mDataBlock->splashFreqMod * dt);
U32 i;
if(!waterWakeHandle)
waterWakeHandle = alxPlay(mDataBlock->waterSound[VehicleData::Wake], &getTransform());
alxSourceMatrixF(waterWakeHandle, &getTransform());
for( i=0; i<VehicleData::VC_NUM_SPLASH_EMITTERS; i++ )
{
if( mSplashEmitterList[i] )
{
mSplashEmitterList[i]->emitParticles( contactPoint, contactPoint, Point3F( 0.0, 0.0, 1.0 ),
moveDir, emitRate );
}
}
}
//--------------------------------------------------------------------------
// Returns true if vehicle is intersecting a water surface (roughly)
//--------------------------------------------------------------------------
bool Vehicle::collidingWithWater( Point3F &waterHeight )
{
Point3F curPos = getPosition();
F32 height = mFabs( mObjBox.max.z - mObjBox.min.z );
RayInfo rInfo;
if( gClientContainer.castRay( curPos + Point3F(0.0, 0.0, height), curPos, WaterObjectType, &rInfo) )
{
waterHeight = rInfo.point;
return true;
}
return false;
}
void Vehicle::setEnergyLevel(F32 energy)
{
Parent::setEnergyLevel(energy);
setMaskBits(EnergyMask);
}
//-----------------------------------------------------------------------------
//
void Vehicle::renderImage(SceneState *state, SceneRenderImage *image)
{
Parent::renderImage(state, image);
if (gShowBoundingBox) {
glDisable(GL_LIGHTING);
glPushMatrix();
dglMultMatrix(&getRenderTransform());
// Box for the center of Mass
glDisable(GL_DEPTH_TEST);
glColor3f(1, 1, 1);
wireCube(Point3F(0.1,0.1,0.1),mDataBlock->massCenter);
glPopMatrix();
// Collision points
for (int i = 0; i < mCollisionList.count; i++) {
glColor3f(0, 0, 1);
Collision& collision = mCollisionList.collision[i];
wireCube(Point3F(0.05,0.05,0.05),collision.point);
glColor3f(1, 1, 1);
glBegin(GL_LINES);
glVertex3fv(collision.point);
glVertex3fv(collision.point + collision.normal * 0.05);
glEnd();
}
// Build and render the collision polylist which is returned
// in the server's world space.
ClippedPolyList polyList;
polyList.mPlaneList.setSize(6);
polyList.mPlaneList[0].set(getWorldBox().min,VectorF(-1,0,0));
polyList.mPlaneList[1].set(getWorldBox().min,VectorF(0,-1,0));
polyList.mPlaneList[2].set(getWorldBox().min,VectorF(0,0,-1));
polyList.mPlaneList[3].set(getWorldBox().max,VectorF(1,0,0));
polyList.mPlaneList[4].set(getWorldBox().max,VectorF(0,1,0));
polyList.mPlaneList[5].set(getWorldBox().max,VectorF(0,0,1));
Box3F dummyBox;
SphereF dummySphere;
buildPolyList(&polyList, dummyBox, dummySphere);
polyList.render();
//
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
}
}
//-----------------------------------------------------------------------------
// Demonstrates adding rendering to mounted objects.
//
void Vehicle::renderMountedImage
(SceneState *state, ShapeImageRenderImage *image)
{
Parent::renderMountedImage(state, image);
if (gShowBoundingBox) {
U32 index = image->mIndex;
if (mMountedImageList[index].dataBlock != NULL) {
Point3F muzzlePoint, muzzleVector, endpoint;
getMuzzlePoint(index, &muzzlePoint);
getMuzzleVector(index, &muzzleVector);
endpoint = muzzlePoint + muzzleVector * 250;
// Lighting has been installed by ShapeBase::renderObject. Switch lighting
// off while rendering the debug graphics.
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(1);
glBegin(GL_LINES);
glColor4ub(0, 255, 0, 255);
glVertex3fv(muzzlePoint);
glVertex3fv(endpoint);
glEnd();
glLineWidth(1);
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);
}
}
}
bool Vehicle::getAIMove(Move *move)
{
return false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -