diff --git a/Engine/source/T3D/item.cpp b/Engine/source/T3D/item.cpp index c7ae784fc..4b592ed78 100644 --- a/Engine/source/T3D/item.cpp +++ b/Engine/source/T3D/item.cpp @@ -50,8 +50,6 @@ const S32 sCollisionTimeout = 15; // Timout value in ticks static F32 sMinWarpTicks = 0.5 ; // Fraction of tick at which instant warp occures static S32 sMaxWarpTicks = 3; // Max warp duration in ticks -F32 Item::mGravity = -20.0f; - const U32 sClientCollisionMask = (TerrainObjectType | StaticShapeObjectType | VehicleObjectType | @@ -716,18 +714,20 @@ void Item::updateWorkingCollisionSet(const U32 mask, const F32 dt) void Item::updateVelocity(const F32 dt) { + // Container buoyancy & drag // Acceleration due to gravity - mVelocity.z += (mGravity * mDataBlock->gravityMod) * dt; + mVelocity.z += (mNetGravity * mDataBlock->gravityMod) * dt; + mVelocity -= mVelocity * mDrag * dt; + + // Add in physical zone force + mVelocity += mAppliedForce; + F32 len; if (mDataBlock->maxVelocity > 0 && (len = mVelocity.len()) > (mDataBlock->maxVelocity * 1.05)) { Point3F excess = mVelocity * (1.0 - (mDataBlock->maxVelocity / len )); excess *= 0.1f; mVelocity -= excess; } - - // Container buoyancy & drag - mVelocity.z -= mBuoyancy * (mGravity * mDataBlock->gravityMod * mGravityMod) * dt; - mVelocity -= mVelocity * mDrag * dt; } diff --git a/Engine/source/T3D/item.h b/Engine/source/T3D/item.h index be79e7f9d..2fe968fc5 100644 --- a/Engine/source/T3D/item.h +++ b/Engine/source/T3D/item.h @@ -92,7 +92,6 @@ class Item: public ShapeBase // Static attributes ItemData* mDataBlock; - static F32 mGravity; bool mStatic; bool mRotate; diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 1ee32b545..6bf41d011 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -1581,8 +1581,6 @@ ConsoleDocClass( Player, "@ingroup gameObjects\n" ); -F32 Player::mGravity = -20; - //---------------------------------------------------------------------------- Player::Player() @@ -2890,7 +2888,7 @@ void Player::updateMove(const Move* move) speed_bias = speed_bias + (speed_bias_goal - speed_bias)*0.1f; moveSpeed *= speed_bias; // Acceleration due to gravity - VectorF acc(0.0f, 0.0f, mGravity * mGravityMod * TickSec); + VectorF acc(0.0f, 0.0f, mNetGravity/(1.0 - mBuoyancy) * TickSec); // Determine ground contact normal. Only look for contacts if // we can move and aren't mounted. @@ -3254,30 +3252,6 @@ void Player::updateMove(const Move* move) mVelocity.z -= mDataBlock->upResistFactor * TickSec * (mVelocity.z - mDataBlock->upResistSpeed); } - // Container buoyancy & drag -/* Commented out until the buoyancy calculation can be reworked so that a container and -** player with the same density will result in neutral buoyancy. - if (mBuoyancy != 0) - { - // Applying buoyancy when standing still causing some jitters- - if (mBuoyancy > 1.0 || !mVelocity.isZero() || !runSurface) - { - // A little hackery to prevent oscillation - // based on http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html - - F32 buoyancyForce = mBuoyancy * mGravity * mGravityMod * TickSec; - F32 currHeight = getPosition().z; - const F32 C = 2.0f; - const F32 M = 0.1f; - - if ( currHeight + mVelocity.z * TickSec * C > mLiquidHeight ) - buoyancyForce *= M; - - mVelocity.z -= buoyancyForce; - } - } -*/ - // Apply drag if ( mSwimming ) mVelocity -= mVelocity * mDrag * TickSec * ( mVelocity.len() / mDataBlock->maxUnderwaterForwardSpeed ); diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h index fed759602..35667fc40 100644 --- a/Engine/source/T3D/player.h +++ b/Engine/source/T3D/player.h @@ -444,7 +444,6 @@ protected: Point3F mRot; ///< Body rotation, uses only z VectorF mVelocity; ///< Velocity Point3F mAnchorPoint; ///< Pos compression anchor - static F32 mGravity; ///< Gravity S32 mImpactSound; bool mUseHeadZCalc; ///< Including mHead.z in transform calculations diff --git a/Engine/source/T3D/rigidShape.cpp b/Engine/source/T3D/rigidShape.cpp index 005ef8c98..be90687f0 100644 --- a/Engine/source/T3D/rigidShape.cpp +++ b/Engine/source/T3D/rigidShape.cpp @@ -47,7 +47,8 @@ #include "sfx/sfxSystem.h" #include "T3D/fx/particleEmitter.h" #include "console/engineAPI.h" - +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsCollision.h" IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData); @@ -152,45 +153,33 @@ ConsoleDocClass( RigidShape, "@ingroup Physics\n" ); +IMPLEMENT_CALLBACK(RigidShapeData, onEnterLiquid, void, (RigidShape* obj, F32 coverage, const char* type), (obj, coverage, type), + "Called when the vehicle enters liquid.\n" + "@param obj the Vehicle object\n" + "@param coverage percentage of the vehicle's bounding box covered by the liquid\n" + "@param type type of liquid the vehicle has entered\n"); -IMPLEMENT_CALLBACK( RigidShape, onEnterLiquid, void, ( const char* objId, F32 waterCoverage, const char* liquidType ), - ( objId, waterCoverage, liquidType ), - "@brief Called whenever this RigidShape object enters liquid.\n\n" - "@param objId The ID of the rigidShape object.\n" - "@param waterCoverage Amount of water coverage the RigidShape has.\n" - "@param liquidType Type of liquid that was entered.\n\n" - "@tsexample\n" - "// The RigidShape object falls in a body of liquid, causing the callback to occur.\n" - "RigidShape::onEnterLiquid(%this,%objId,%waterCoverage,%liquidType)\n" - " {\n" - " // Code to run whenever this callback occurs.\n" - " }\n" - "@endtsexample\n\n" - "@see ShapeBase\n\n" -); - -IMPLEMENT_CALLBACK( RigidShape, onLeaveLiquid, void, ( const char* objId, const char* liquidType ),( objId, liquidType ), - "@brief Called whenever the RigidShape object exits liquid.\n\n" - "@param objId The ID of the RigidShape object.\n" - "@param liquidType Type if liquid that was exited.\n\n" - "@tsexample\n" - "// The RigidShape object exits in a body of liquid, causing the callback to occur.\n" - "RigidShape::onLeaveLiquid(%this,%objId,%liquidType)\n" - " {\n" - " // Code to run whenever this callback occurs.\n" - " }\n" - "@endtsexample\n\n" - "@see ShapeBase\n\n" -); +IMPLEMENT_CALLBACK(RigidShapeData, onLeaveLiquid, void, (RigidShape* obj, const char* type), (obj, type), + "Called when the vehicle leaves liquid.\n" + "@param obj the Vehicle object\n" + "@param type type of liquid the vehicle has left\n"); //---------------------------------------------------------------------------- namespace { + static U32 sWorkingQueryBoxStaleThreshold = 10; // The maximum number of ticks that go by before + // the mWorkingQueryBox is considered stale and + // needs updating. Set to -1 to disable. + + static F32 sWorkingQueryBoxSizeMultiplier = 2.0f; // How much larger should the mWorkingQueryBox be + // made when updating the working collision list. + // The larger this number the less often the working list + // will be updated due to motion, but any non-static shape + // that moves into the query box will not be noticed. // Client prediction const S32 sMaxWarpTicks = 3; // Max warp duration in ticks const S32 sMaxPredictionTicks = 30; // Number of ticks to predict - const F32 sRigidShapeGravity = -20; // Physics and collision constants static F32 sRestTol = 0.5; // % of gravity energy to be at rest @@ -265,6 +254,7 @@ RigidShapeData::RigidShapeData() softSplashSoundVel = 1.0; medSplashSoundVel = 2.0; hardSplashSoundVel = 3.0; + enablePhysicsRep = true; dMemset(waterSound, 0, sizeof(waterSound)); @@ -390,6 +380,7 @@ void RigidShapeData::packData(BitStream* stream) stream->write(softSplashSoundVel); stream->write(medSplashSoundVel); stream->write(hardSplashSoundVel); + stream->write(enablePhysicsRep); // write the water sound profiles for( U32 i = 0; i < MaxSounds; ++ i ) @@ -448,6 +439,7 @@ void RigidShapeData::unpackData(BitStream* stream) stream->read(&softSplashSoundVel); stream->read(&medSplashSoundVel); stream->read(&hardSplashSoundVel); + stream->read(&enablePhysicsRep); // write the water sound profiles for( U32 i = 0; i < MaxSounds; ++ i ) @@ -477,6 +469,11 @@ void RigidShapeData::unpackData(BitStream* stream) void RigidShapeData::initPersistFields() { + addGroup("Physics"); + addField("enablePhysicsRep", TypeBool, Offset(enablePhysicsRep, RigidShapeData), + "@brief Creates a representation of the object in the physics plugin.\n"); + endGroup("Physics"); + addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body."); addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box."); addField("bodyRestitution", TypeF32, Offset(body.restitution, RigidShapeData), "The percentage of kinetic energy kept by this object in a collision."); @@ -592,6 +589,12 @@ RigidShape::RigidShape() restCount = 0; inLiquid = false; + + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; + + mPhysicsRep = NULL; } RigidShape::~RigidShape() @@ -619,6 +622,9 @@ bool RigidShape::onAdd() if (!Parent::onAdd()) return false; + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + // 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); @@ -672,6 +678,7 @@ bool RigidShape::onAdd() mConvex.box.minExtents.convolve(mObjScale); mConvex.box.maxExtents.convolve(mObjScale); mConvex.findNodeTransform(); + _createPhysics(); addToScene(); @@ -722,14 +729,36 @@ void RigidShape::onRemove() } } + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); Parent::onRemove(); } +void RigidShape::_createPhysics() +{ + SAFE_DELETE(mPhysicsRep); + + if (!PHYSICSMGR || !mDataBlock->enablePhysicsRep) + return; + + TSShape* shape = mShapeInstance->getShape(); + PhysicsCollision* colShape = NULL; + colShape = shape->buildColShape(false, getScale()); + + if (colShape) + { + PhysicsWorld* world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client"); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init(colShape, 0, PhysicsBody::BF_KINEMATIC, this, world); + mPhysicsRep->setTransform(getTransform()); + } +} //---------------------------------------------------------------------------- - void RigidShape::processTick(const Move* move) { + PROFILE_SCOPE(RigidShape_ProcessTick); + Parent::processTick(move); if ( isMounted() ) return; @@ -776,6 +805,8 @@ void RigidShape::processTick(const Move* move) // Update the physics based on the integration rate S32 count = mDataBlock->integration; + --mWorkingQueryBoxCountDown; + if (!mDisableMove) updateWorkingCollisionSet(getCollisionMask()); for (U32 i = 0; i < count; i++) @@ -790,6 +821,11 @@ void RigidShape::processTick(const Move* move) setPosition(mRigid.linPosition, mRigid.angPosition); setMaskBits(PositionMask); updateContainer(); + + //TODO: Only update when position has actually changed + //no need to check if mDataBlock->enablePhysicsRep is false as mPhysicsRep will be NULL if it is + if (mPhysicsRep) + mPhysicsRep->moveKinematicTo(getTransform()); } } @@ -1036,6 +1072,8 @@ void RigidShape::enableCollision() void RigidShape::updatePos(F32 dt) { + PROFILE_SCOPE(Vehicle_UpdatePos); + Point3F origVelocity = mRigid.linVelocity; // Update internal forces acting on the body. @@ -1044,19 +1082,19 @@ void RigidShape::updatePos(F32 dt) // Update collision information based on our current pos. bool collided = false; - if (!mRigid.atRest && !mDisableMove) + if (!mRigid.atRest && !mDisableMove) { collided = updateCollision(dt); - // Now that all the forces have been processed, lets + // Now that all the forces have been processed, lets // see if we're at rest. Basically, if the kinetic energy of - // the shape is less than some percentage of the energy added + // the rigid body 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.getCount()) + if (mCollisionList.getCount()) { F32 k = mRigid.getKineticEnergy(); - F32 G = sRigidShapeGravity * dt; + F32 G = mNetGravity * dt; F32 Kg = 0.5 * mRigid.mass * G * G; if (k < sRestTol * Kg && ++restCount > sRestCount) mRigid.setAtRest(); @@ -1070,7 +1108,7 @@ void RigidShape::updatePos(F32 dt) mRigid.integrate(dt); // Deal with client and server scripting, sounds, etc. - if (isServerObject()) + if (isServerObject()) { // Check triggers and other objects that we normally don't @@ -1083,7 +1121,7 @@ void RigidShape::updatePos(F32 dt) notifyCollision(); // Server side impact script callback - if (collided) + if (collided) { VectorF collVec = mRigid.linVelocity - origVelocity; F32 collSpeed = collVec.len(); @@ -1091,15 +1129,15 @@ void RigidShape::updatePos(F32 dt) onImpact(collVec); } - // Water script callbacks - if (!inLiquid && mWaterCoverage != 0.0f) + // Water script callbacks + if (!inLiquid && mWaterCoverage != 0.0f) { - onEnterLiquid_callback(getIdString(), mWaterCoverage, mLiquidType.c_str() ); + mDataBlock->onEnterLiquid_callback(this, mWaterCoverage, mLiquidType.c_str()); inLiquid = true; } - else if (inLiquid && mWaterCoverage == 0.0f) + else if (inLiquid && mWaterCoverage == 0.0f) { - onLeaveLiquid_callback(getIdString(), mLiquidType.c_str() ); + mDataBlock->onLeaveLiquid_callback(this, mLiquidType.c_str()); inLiquid = false; } @@ -1123,7 +1161,7 @@ void RigidShape::updatePos(F32 dt) // Water volume sounds F32 vSpeed = getVelocity().len(); if (!inLiquid && mWaterCoverage >= 0.8f) { - if (vSpeed >= mDataBlock->hardSplashSoundVel) + if (vSpeed >= mDataBlock->hardSplashSoundVel) SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactHard], &getTransform()); else if (vSpeed >= mDataBlock->medSplashSoundVel) @@ -1132,9 +1170,9 @@ void RigidShape::updatePos(F32 dt) if (vSpeed >= mDataBlock->softSplashSoundVel) SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactSoft], &getTransform()); inLiquid = true; - } + } else - if(inLiquid && mWaterCoverage < 0.8f) { + if (inLiquid && mWaterCoverage < 0.8f) { if (vSpeed >= mDataBlock->exitSplashSoundVel) SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ExitWater], &getTransform()); inLiquid = false; @@ -1142,40 +1180,31 @@ void RigidShape::updatePos(F32 dt) } } - //---------------------------------------------------------------------------- -void RigidShape::updateForces(F32 /*dt*/) +void RigidShape::updateForces(F32 dt) { if (mDisableMove) return; - Point3F gravForce(0, 0, sRigidShapeGravity * mRigid.mass * mGravityMod); - - MatrixF currTransform; - mRigid.getTransform(&currTransform); Point3F torque(0, 0, 0); - Point3F force(0, 0, 0); - - Point3F vel = mRigid.linVelocity; - - // Gravity - force += gravForce; + Point3F force(0, 0, mRigid.mass * mNetGravity); // Apply drag - Point3F vDrag = mRigid.linVelocity; - vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor)); - force -= vDrag * mDataBlock->dragForce; + Point3F vertDrag = mRigid.linVelocity*Point3F(1, 1, mDataBlock->vertFactor); + force -= vertDrag * mDataBlock->dragForce; // Add in physical zone force force += mAppliedForce; - // Container buoyancy & drag - force += Point3F(0, 0,-mBuoyancy * sRigidShapeGravity * mRigid.mass * mGravityMod); force -= mRigid.linVelocity * mDrag; torque -= mRigid.angMomentum * mDrag; mRigid.force = force; mRigid.torque = torque; + + // If we're still atRest, make sure we're not accumulating anything + if (mRigid.atRest) + mRigid.setAtRest(); } @@ -1369,17 +1398,53 @@ bool RigidShape::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt) void RigidShape::updateWorkingCollisionSet(const U32 mask) { + PROFILE_SCOPE( Vehicle_UpdateWorkingCollisionSet ); + + // 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.minExtents -= Point3F(l, l, l); convexBox.maxExtents += Point3F(l, l, l); - disableCollision(); - mConvex.updateWorkingList(convexBox, mask); - enableCollision(); -} + // Check to see if it is actually necessary to construct the new working list, + // or if we can use the cached version from the last query. We use the x + // component of the min member of the mWorkingQueryBox, which is lame, but + // it works ok. + bool updateSet = false; + // Check containment + if ((sWorkingQueryBoxStaleThreshold == -1 || mWorkingQueryBoxCountDown > 0) && mWorkingQueryBox.minExtents.x != -1e9f) + { + if (mWorkingQueryBox.isContained(convexBox) == false) + // Needed region is outside the cached region. Update it. + updateSet = true; + } + else + { + // Must update + updateSet = true; + } + + // Actually perform the query, if necessary + if (updateSet == true) + { + mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; + + const Point3F lPoint( sWorkingQueryBoxSizeMultiplier * l ); + mWorkingQueryBox = convexBox; + mWorkingQueryBox.minExtents -= lPoint; + mWorkingQueryBox.maxExtents += lPoint; + + disableCollision(); + mConvex.updateWorkingList(mWorkingQueryBox, mask); + enableCollision(); + } +} //---------------------------------------------------------------------------- /** Check collisions with trigger and items @@ -1573,6 +1638,25 @@ void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream) //---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +void RigidShape::consoleInit() +{ + Con::addVariable("$rigidPhysics::workingQueryBoxStaleThreshold", TypeS32, &sWorkingQueryBoxStaleThreshold, + "@brief The maximum number of ticks that go by before the mWorkingQueryBox is considered stale and needs updating.\n\n" + "Other factors can cause the collision working query box to become invalidated, such as the rigid body moving far " + "enough outside of this cached box. The smaller this number, the more times the working list of triangles that are " + "considered for collision is refreshed. This has the greatest impact with colliding with high triangle count meshes.\n\n" + "@note Set to -1 to disable any time-based forced check.\n\n" + "@ingroup GameObjects\n"); + + Con::addVariable("$rigidPhysics::workingQueryBoxSizeMultiplier", TypeF32, &sWorkingQueryBoxSizeMultiplier, + "@brief How much larger the mWorkingQueryBox should be made when updating the working collision list.\n\n" + "The larger this number the less often the working list will be updated due to motion, but any non-static shape that " + "moves into the query box will not be noticed.\n\n" + "@ingroup GameObjects\n"); +} + void RigidShape::initPersistFields() { Parent::initPersistFields(); diff --git a/Engine/source/T3D/rigidShape.h b/Engine/source/T3D/rigidShape.h index 6f6b9fcea..1290661f2 100644 --- a/Engine/source/T3D/rigidShape.h +++ b/Engine/source/T3D/rigidShape.h @@ -31,12 +31,14 @@ #ifndef _BOXCONVEX_H_ #include "collision/boxConvex.h" #endif +#ifndef _T3D_PHYSICS_PHYSICSBODY_H_ +#include "T3D/physics/physicsBody.h" +#endif class ParticleEmitter; class ParticleEmitterData; class ClippedPolyList; - class RigidShapeData : public ShapeBaseData { typedef ShapeBaseData Parent; @@ -112,6 +114,8 @@ class RigidShapeData : public ShapeBaseData F32 splashFreqMod; F32 splashVelEpsilon; + bool enablePhysicsRep; + F32 dragForce; F32 vertFactor; @@ -132,6 +136,9 @@ class RigidShapeData : public ShapeBaseData DECLARE_CONOBJECT(RigidShapeData); + DECLARE_CALLBACK(void, onEnterLiquid, (RigidShape* obj, F32 coverage, const char* type)); + DECLARE_CALLBACK(void, onLeaveLiquid, (RigidShape* obj, const char* type)); + }; @@ -177,6 +184,8 @@ class RigidShape: public ShapeBase Point3F cameraRotVec; }; + PhysicsBody* mPhysicsRep; + StateDelta mDelta; S32 mPredictionCount; ///< Number of ticks to predict bool inLiquid; @@ -196,6 +205,9 @@ class RigidShape: public ShapeBase GFXStateBlockRef mSolidSB; + Box3F mWorkingQueryBox; + S32 mWorkingQueryBoxCountDown; + // bool onNewDataBlock( GameBaseData *dptr, bool reload ); void updatePos(F32 dt); @@ -203,7 +215,6 @@ class RigidShape: public ShapeBase bool resolveCollision(Rigid& ns,CollisionList& cList); bool resolveContacts(Rigid& ns,CollisionList& cList,F32 dt); bool resolveDisplacement(Rigid& ns,CollisionState *state,F32 dt); - bool findContacts(Rigid& ns,CollisionList& cList); void checkTriggers(); static void findCallback(SceneObject* obj,void * key); @@ -227,7 +238,7 @@ class RigidShape: public ShapeBase void _renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); - void updateForces(F32); + void updateForces(F32 dt); public: // Test code... @@ -238,11 +249,13 @@ public: RigidShape(); ~RigidShape(); + static void consoleInit(); static void initPersistFields(); void processTick(const Move *move); bool onAdd(); void onRemove(); - + void _createPhysics(); + /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function void interpolateTick(F32 dt); @@ -291,8 +304,6 @@ public: void unpackUpdate(NetConnection *conn, BitStream *stream); DECLARE_CONOBJECT(RigidShape); - DECLARE_CALLBACK( void, onEnterLiquid, ( const char* objId, F32 waterCoverage, const char* liquidType )); - DECLARE_CALLBACK( void, onLeaveLiquid, ( const char* objId, const char* liquidType )); }; diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 5bac35033..241853879 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -1000,7 +1000,7 @@ ShapeBase::ShapeBase() mLiquidHeight( 0.0f ), mWaterCoverage( 0.0f ), mAppliedForce( Point3F::Zero ), - mGravityMod( 1.0f ), + mNetGravity( 1.0f ), mDamageFlash( 0.0f ), mWhiteOut( 0.0f ), mFlipFadeVal( false ), @@ -1768,7 +1768,7 @@ void ShapeBase::updateContainer() // Set default values. mDrag = mDataBlock->drag; mBuoyancy = 0.0f; - mGravityMod = 1.0; + mNetGravity = gGravity; mAppliedForce.set(0,0,0); ContainerQueryInfo info; @@ -1797,7 +1797,7 @@ void ShapeBase::updateContainer() } mAppliedForce = info.appliedForce; - mGravityMod = info.gravityScale; + mNetGravity = (1.0-mBuoyancy)*info.gravityScale* gGravity; //Con::printf( "WaterCoverage: %f", mWaterCoverage ); //Con::printf( "Drag: %f", mDrag ); diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 71b221036..50da9a293 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -86,6 +86,7 @@ class SFXProfile; typedef void* Light; +const F32 gGravity = -20; //-------------------------------------------------------------------------- @@ -914,7 +915,7 @@ protected: F32 mWaterCoverage; ///< Percent of this object covered by water Point3F mAppliedForce; - F32 mGravityMod; + F32 mNetGravity; /// @} F32 mDamageFlash; diff --git a/Engine/source/T3D/vehicles/flyingVehicle.cpp b/Engine/source/T3D/vehicles/flyingVehicle.cpp index ffdebc673..e092668d8 100644 --- a/Engine/source/T3D/vehicles/flyingVehicle.cpp +++ b/Engine/source/T3D/vehicles/flyingVehicle.cpp @@ -50,8 +50,6 @@ const static U32 sCollisionMoveMask = ( TerrainObjectType | WaterObjectType static U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType static U32 sClientCollisionMask = sCollisionMoveMask; -static F32 sFlyingVehicleGravity = -20.0f; - // const char* FlyingVehicle::sJetSequence[FlyingVehicle::JetAnimCount] = { @@ -485,6 +483,7 @@ void FlyingVehicle::updateForces(F32 /*dt*/) { PROFILE_SCOPE( FlyingVehicle_UpdateForces ); + if (mDisableMove) return; MatrixF currPosMat; mRigid.getTransform(&currPosMat); mRigid.atRest = false; @@ -498,7 +497,7 @@ void FlyingVehicle::updateForces(F32 /*dt*/) currPosMat.getColumn(2,&zv); F32 speed = mRigid.linVelocity.len(); - Point3F force = Point3F(0, 0, sFlyingVehicleGravity * mRigid.mass * mGravityMod); + Point3F force = Point3F(0, 0, mRigid.mass * mNetGravity); Point3F torque = Point3F(0, 0, 0); // Drag at any speed @@ -520,7 +519,7 @@ void FlyingVehicle::updateForces(F32 /*dt*/) } // Hovering Jet - F32 vf = -sFlyingVehicleGravity * mRigid.mass * mGravityMod; + F32 vf = mRigid.mass * -mNetGravity; F32 h = getHeight(); if (h <= 1) { if (h > 0) { @@ -567,8 +566,6 @@ void FlyingVehicle::updateForces(F32 /*dt*/) // Add in force from physical zones... force += mAppliedForce; - // Container buoyancy & drag - force -= Point3F(0, 0, 1) * (mBuoyancy * sFlyingVehicleGravity * mRigid.mass * mGravityMod); force -= mRigid.linVelocity * mDrag; // diff --git a/Engine/source/T3D/vehicles/hoverVehicle.cpp b/Engine/source/T3D/vehicles/hoverVehicle.cpp index a50ed7a42..ee49b917f 100644 --- a/Engine/source/T3D/vehicles/hoverVehicle.cpp +++ b/Engine/source/T3D/vehicles/hoverVehicle.cpp @@ -69,7 +69,6 @@ ConsoleDocClass( HoverVehicle, ); namespace { -const F32 sHoverVehicleGravity = -20; const U32 sCollisionMoveMask = (TerrainObjectType | PlayerObjectType | StaticShapeObjectType | VehicleObjectType | @@ -674,7 +673,7 @@ void HoverVehicle::updateForces(F32 /*dt*/) { PROFILE_SCOPE( HoverVehicle_UpdateForces ); - Point3F gravForce(0, 0, sHoverVehicleGravity * mRigid.mass * mGravityMod); + Point3F gravForce(0, 0, mRigid.mass * mNetGravity); MatrixF currTransform; mRigid.getTransform(&currTransform); @@ -872,8 +871,6 @@ void HoverVehicle::updateForces(F32 /*dt*/) // Add in physical zone force force += mAppliedForce; - // Container buoyancy & drag - force += Point3F(0, 0,-mBuoyancy * sHoverVehicleGravity * mRigid.mass * mGravityMod); force -= mRigid.linVelocity * mDrag; torque -= mRigid.angMomentum * mDrag; diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index 9d7809a26..cc05b823e 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -66,7 +66,6 @@ static F32 sWorkingQueryBoxSizeMultiplier = 2.0f; // How much larger should the // Client prediction const S32 sMaxWarpTicks = 3; // Max warp duration in ticks const S32 sMaxPredictionTicks = 30; // Number of ticks to predict -const F32 sVehicleGravity = -20; // Physics and collision constants static F32 sRestTol = 0.5; // % of gravity energy to be at rest @@ -124,17 +123,6 @@ ConsoleDocClass( VehicleData, "@ingroup Vehicles\n" ); -IMPLEMENT_CALLBACK( VehicleData, onEnterLiquid, void, ( Vehicle* obj, F32 coverage, const char* type ), ( obj, coverage, type ), - "Called when the vehicle enters liquid.\n" - "@param obj the Vehicle object\n" - "@param coverage percentage of the vehicle's bounding box covered by the liquid\n" - "@param type type of liquid the vehicle has entered\n" ); - -IMPLEMENT_CALLBACK( VehicleData, onLeaveLiquid, void, ( Vehicle* obj, const char* type ), ( obj, type ), - "Called when the vehicle leaves liquid.\n" - "@param obj the Vehicle object\n" - "@param type type of liquid the vehicle has left\n" ); - //---------------------------------------------------------------------------- VehicleData::VehicleData() @@ -678,9 +666,7 @@ Vehicle::Vehicle() mCameraOffset.set(0,0,0); - dMemset( mDustEmitterList, 0, sizeof( mDustEmitterList ) ); dMemset( mDamageEmitterList, 0, sizeof( mDamageEmitterList ) ); - dMemset( mSplashEmitterList, 0, sizeof( mSplashEmitterList ) ); mDisableMove = false; restCount = 0; @@ -701,30 +687,6 @@ U32 Vehicle::getCollisionMask() return 0; } -Point3F Vehicle::getVelocity() const -{ - return mRigid.linVelocity; -} - -void Vehicle::_createPhysics() -{ - SAFE_DELETE(mPhysicsRep); - - if (!PHYSICSMGR || !mDataBlock->enablePhysicsRep) - return; - - TSShape *shape = mShapeInstance->getShape(); - PhysicsCollision *colShape = NULL; - colShape = shape->buildColShape(false, getScale()); - - if (colShape) - { - PhysicsWorld *world = PHYSICSMGR->getWorld(isServerObject() ? "server" : "client"); - mPhysicsRep = PHYSICSMGR->createBody(); - mPhysicsRep->init(colShape, 0, PhysicsBody::BF_KINEMATIC, this, world); - mPhysicsRep->setTransform(getTransform()); - } -} //---------------------------------------------------------------------------- bool Vehicle::onAdd() @@ -747,21 +709,6 @@ bool Vehicle::onAdd() // Create Emitters on the client if( isClientObject() ) { - if( mDataBlock->dustEmitter ) - { - for( S32 i=0; ionNewDataBlock( mDataBlock->dustEmitter, false ); - 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; jsplashEmitterList[j] ) - { - mSplashEmitterList[j] = new ParticleEmitter; - mSplashEmitterList[j]->onNewDataBlock( mDataBlock->splashEmitterList[j], false ); - 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. @@ -909,26 +840,6 @@ void Vehicle::processTick(const Move* move) } } -void Vehicle::interpolateTick(F32 dt) -{ - PROFILE_SCOPE( Vehicle_InterpolateTick ); - - Parent::interpolateTick(dt); - if ( isMounted() ) - return; - - 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) { PROFILE_SCOPE( Vehicle_AdvanceTime ); @@ -1080,22 +991,6 @@ void Vehicle::getCameraTransform(F32* pos, MatrixF* mat) mat->mul( gCamFXMgr.getTrans() ); } - -//---------------------------------------------------------------------------- - -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) @@ -1161,51 +1056,6 @@ void Vehicle::updateMove(const Move* move) 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.clear(); -} - - -//----------------------------------------------------------------------------- - -void Vehicle::disableCollision() -{ - Parent::disableCollision(); - for (SceneObject* ptr = getMountList(); ptr; ptr = ptr->getMountLink()) - ptr->disableCollision(); -} - -void Vehicle::enableCollision() -{ - Parent::enableCollision(); - for (SceneObject* ptr = getMountList(); ptr; ptr = ptr->getMountLink()) - ptr->enableCollision(); -} - - //---------------------------------------------------------------------------- /** Update the physics */ @@ -1234,7 +1084,7 @@ void Vehicle::updatePos(F32 dt) if (mCollisionList.getCount()) { F32 k = mRigid.getKineticEnergy(); - F32 G = sVehicleGravity * dt; + F32 G = mNetGravity * dt; F32 Kg = 0.5 * mRigid.mass * G * G; if (k < sRestTol * Kg && ++restCount > sRestCount) mRigid.setAtRest(); @@ -1325,101 +1175,6 @@ void Vehicle::updateForces(F32 /*dt*/) } -//----------------------------------------------------------------------------- -/** 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) -{ - PROFILE_SCOPE( Vehicle_UpdateCollision ); - - // Update collision information - MatrixF mat,cmat; - mConvex.transform = &mat; - mRigid.getTransform(&mat); - cmat = mConvex.getTransform(); - - mCollisionList.clear(); - CollisionState *state = mConvex.findClosestState(cmat, getScale(), mDataBlock->collisionTol); - if (state && state->mDist <= 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; -} - -//---------------------------------------------------------------------------- - -void Vehicle::updateWorkingCollisionSet(const U32 mask) -{ - PROFILE_SCOPE( Vehicle_UpdateWorkingCollisionSet ); - - // 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.minExtents -= Point3F(l, l, l); - convexBox.maxExtents += Point3F(l, l, l); - - // Check to see if it is actually necessary to construct the new working list, - // or if we can use the cached version from the last query. We use the x - // component of the min member of the mWorkingQueryBox, which is lame, but - // it works ok. - bool updateSet = false; - - // Check containment - if ((sWorkingQueryBoxStaleThreshold == -1 || mWorkingQueryBoxCountDown > 0) && mWorkingQueryBox.minExtents.x != -1e9f) - { - if (mWorkingQueryBox.isContained(convexBox) == false) - // Needed region is outside the cached region. Update it. - updateSet = true; - } - else - { - // Must update - updateSet = true; - } - - // Actually perform the query, if necessary - if (updateSet == true) - { - mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; - - const Point3F lPoint( sWorkingQueryBoxSizeMultiplier * l ); - mWorkingQueryBox = convexBox; - mWorkingQueryBox.minExtents -= lPoint; - mWorkingQueryBox.maxExtents += lPoint; - - disableCollision(); - mConvex.updateWorkingList(mWorkingQueryBox, 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. @@ -1743,32 +1498,6 @@ void Vehicle::updateFroth( F32 dt ) } - -//-------------------------------------------------------------------------- -// Returns true if vehicle is intersecting a water surface (roughly) -//-------------------------------------------------------------------------- -bool Vehicle::collidingWithWater( Point3F &waterHeight ) -{ - Point3F curPos = getPosition(); - - F32 height = mFabs( mObjBox.maxExtents.z - mObjBox.minExtents.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::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ) { Parent::prepBatchRender( state, mountedImageIndex ); diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h index 490e1a5a8..585f81b90 100644 --- a/Engine/source/T3D/vehicles/vehicle.h +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -131,9 +131,6 @@ struct VehicleData : public RigidShapeData virtual void unpackData(BitStream* stream); DECLARE_CONOBJECT(VehicleData); - - DECLARE_CALLBACK( void, onEnterLiquid, ( Vehicle* obj, F32 coverage, const char* type ) ); - DECLARE_CALLBACK( void, onLeaveLiquid, ( Vehicle* obj, const char* type ) ); }; @@ -145,74 +142,24 @@ class Vehicle : public RigidShape typedef RigidShape Parent; protected: - enum CollisionFaceFlags { - BodyCollision = 0x1, - WheelCollision = 0x2, - }; - struct StateDelta { - Move move; ///< Last move from server - F32 dt; ///< Last interpolation time - // Interpolation data - Point3F pos; - Point3F posVec; - QuatF rot[2]; - // Warp data - S32 warpTicks; ///< Number of ticks to warp - S32 warpCount; ///< Current pos in warp - Point3F warpOffset; - QuatF warpRot[2]; - // - Point3F cameraOffset; - Point3F cameraVec; - Point3F cameraRot; - Point3F cameraRotVec; - }; - - PhysicsBody *mPhysicsRep; - - StateDelta mDelta; - S32 mPredictionCount; ///< Number of ticks to predict VehicleData* mDataBlock; - bool inLiquid; SFXSource* mWakeSound; - Point3F mCameraOffset; ///< 3rd person camera - // Control Point2F mSteering; F32 mThrottle; bool mJetting; - // Rigid Body - bool mDisableMove; - GFXStateBlockRef mSolidSB; - Box3F mWorkingQueryBox; - S32 mWorkingQueryBoxCountDown; - - CollisionList mCollisionList; - CollisionList mContacts; - ShapeBaseConvex mConvex; - S32 restCount; - - SimObjectPtr mDustEmitterList[VehicleData::VC_NUM_DUST_EMITTERS]; SimObjectPtr mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS]; - SimObjectPtr mSplashEmitterList[VehicleData::VC_NUM_SPLASH_EMITTERS]; // virtual bool onNewDataBlock( GameBaseData *dptr, bool reload ); void updatePos(F32 dt); - bool updateCollision(F32 dt); - bool findContacts(Rigid& ns,CollisionList& cList); - void checkTriggers(); static void findCallback(SceneObject* obj,void * key); - void setPosition(const Point3F& pos,const QuatF& rot); - void setRenderPosition(const Point3F& pos,const QuatF& rot); - void setTransform(const MatrixF& mat); - // virtual bool collideBody(const MatrixF& mat,Collision* info) = 0; virtual void updateMove(const Move* move); virtual void updateForces(F32 dt); @@ -225,11 +172,9 @@ class Vehicle : public RigidShape void updateLiftoffDust( F32 dt ); void updateDamageSmoke( F32 dt ); - void updateWorkingCollisionSet(const U32 mask); virtual U32 getCollisionMask(); void updateFroth( F32 dt ); - bool collidingWithWater( Point3F &waterHeight ); /// ObjectRenderInst delegate hooked up in prepBatchRender /// if GameBase::gShowBoundingBox is true. @@ -252,40 +197,15 @@ public: bool onAdd(); void onRemove(); - void _createPhysics(); - /// Interpolates between move ticks @see processTick /// @param dt Change in time between the last call and this call to the function - void interpolateTick(F32 dt); void advanceTime(F32 dt); - /// Disables collisions for this vehicle and all mounted objects - void disableCollision(); - - /// Enables collisions for this vehicle and all mounted objects - void enableCollision(); - - /// Returns the velocity of the vehicle - Point3F getVelocity() const; - - void setEnergyLevel(F32 energy); - void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); ///@name Rigid body methods ///@{ - /// This method will get the velocity of the object, taking into account - /// angular velocity. - /// @param r Point on the object you want the velocity of, relative to Center of Mass - /// @param vel Velocity (out) - void getVelocity(const Point3F& r, Point3F* vel); - - /// Applies an impulse force - /// @param r Point on the object to apply impulse to, r is relative to Center of Mass - /// @param impulse Impulse vector to apply. - void applyImpulse(const Point3F &r, const Point3F &impulse); - void getCameraParameters(F32 *min, F32* max, Point3F* offset, MatrixF* rot); void getCameraTransform(F32* pos, MatrixF* mat); ///@} diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.cpp b/Engine/source/T3D/vehicles/wheeledVehicle.cpp index 3e9d686a2..7374c51d1 100644 --- a/Engine/source/T3D/vehicles/wheeledVehicle.cpp +++ b/Engine/source/T3D/vehicles/wheeledVehicle.cpp @@ -54,9 +54,6 @@ static U32 sClientCollisionMask = StaticShapeObjectType | VehicleObjectType | VehicleBlockerObjectType; -// Gravity constant -static F32 sWheeledVehicleGravity = -20; - // Misc. sound constants static F32 sMinSquealVolume = 0.05f; static F32 sIdleEngineVolume = 0.2f; @@ -857,6 +854,7 @@ void WheeledVehicle::updateForces(F32 dt) extendWheels(); + if (mDisableMove) return; F32 aMomentum = mMass / mDataBlock->wheelCount; // Get the current matrix and extact vectors @@ -902,8 +900,7 @@ void WheeledVehicle::updateForces(F32 dt) // Integrate forces, we'll do this ourselves here instead of // relying on the rigid class which does it during movement. Wheel* wend = &mWheel[mDataBlock->wheelCount]; - mRigid.force.set(0, 0, 0); - mRigid.torque.set(0, 0, 0); + mRigid.clearForces(); // Calculate vertical load for friction. Divide up the spring // forces across all the wheels that are in contact with @@ -1095,7 +1092,7 @@ void WheeledVehicle::updateForces(F32 dt) mRigid.force += mAppliedForce; // Container drag & buoyancy - mRigid.force += Point3F(0, 0, -mBuoyancy * sWheeledVehicleGravity * mRigid.mass); + mRigid.force += Point3F(0, 0, mRigid.mass * mNetGravity); mRigid.force -= mRigid.linVelocity * mDrag; mRigid.torque -= mRigid.angMomentum * mDrag; @@ -1104,17 +1101,13 @@ void WheeledVehicle::updateForces(F32 dt) if (mRigid.atRest && (mRigid.force.len() || mRigid.torque.len())) mRigid.atRest = false; - // Gravity - mRigid.force += Point3F(0, 0, sWheeledVehicleGravity * mRigid.mass); - // Integrate and update velocity mRigid.linMomentum += mRigid.force * dt; mRigid.angMomentum += mRigid.torque * dt; mRigid.updateVelocity(); // Since we've already done all the work, just need to clear this out. - mRigid.force.set(0, 0, 0); - mRigid.torque.set(0, 0, 0); + mRigid.clearForces(); // If we're still atRest, make sure we're not accumulating anything if (mRigid.atRest)