void RigidBody::SetTransform(const Vector3& position, const Quaternion& rotation) { if (body_) { btTransform& worldTrans = body_->getWorldTransform(); worldTrans.setRotation(ToBtQuaternion(rotation)); worldTrans.setOrigin(ToBtVector3(position + rotation * centerOfMass_)); if (!hasSimulated_ || physicsWorld_->IsSimulating()) { btTransform interpTrans = body_->getInterpolationWorldTransform(); interpTrans.setOrigin(worldTrans.getOrigin()); interpTrans.setRotation(worldTrans.getRotation()); body_->setInterpolationWorldTransform(interpTrans); } body_->updateInertiaTensor(); Activate(); MarkNetworkUpdate(); } }
void RigidBody::SetPosition(const Vector3& position) { if (body_) { btTransform& worldTrans = body_->getWorldTransform(); worldTrans.setOrigin(ToBtVector3(position + ToQuaternion(worldTrans.getRotation()) * centerOfMass_)); // When forcing the physics position, set also interpolated position so that there is no jitter // When not inside the simulation loop, this may lead to erratic movement of parented rigidbodies // so skip in that case. Exception made before first simulation tick so that interpolation position // of e.g. instantiated prefabs will be correct from the start if (!hasSimulated_ || physicsWorld_->IsSimulating()) { btTransform interpTrans = body_->getInterpolationWorldTransform(); interpTrans.setOrigin(worldTrans.getOrigin()); body_->setInterpolationWorldTransform(interpTrans); } Activate(); MarkNetworkUpdate(); } }
void RigidBody::SetRotation(Quaternion rotation) { if (body_) { // Due to center of mass offset, we may need to adjust position also Vector3 oldPosition = GetPosition(); btTransform& worldTrans = body_->getWorldTransform(); worldTrans.setRotation(ToBtQuaternion(rotation)); if (!centerOfMass_.Equals(Vector3::ZERO)) worldTrans.setOrigin(ToBtVector3(oldPosition + rotation * centerOfMass_)); btTransform interpTrans = body_->getInterpolationWorldTransform(); interpTrans.setRotation(worldTrans.getRotation()); if (!centerOfMass_.Equals(Vector3::ZERO)) interpTrans.setOrigin(worldTrans.getOrigin()); body_->setInterpolationWorldTransform(interpTrans); body_->updateInertiaTensor(); Activate(); MarkNetworkUpdate(); } }
void CollisionShape::DrawDebugGeometry(DebugRenderer* debug, bool depthTest) { if (debug && physicsWorld_ && shape_ && node_ && IsEnabledEffective()) { physicsWorld_->SetDebugRenderer(debug); physicsWorld_->SetDebugDepthTest(depthTest); // Use the rigid body's world transform if possible, as it may be different from the rendering transform Matrix3x4 worldTransform; RigidBody* body = GetComponent<RigidBody>(); bool bodyActive = false; if (body) { worldTransform = Matrix3x4(body->GetPosition(), body->GetRotation(), node_->GetWorldScale()); bodyActive = body->IsActive(); } else worldTransform = node_->GetWorldTransform(); Vector3 position = position_; // For terrains, undo the height centering performed automatically by Bullet if (shapeType_ == SHAPE_TERRAIN && geometry_) { HeightfieldData* heightfield = static_cast<HeightfieldData*>(geometry_.Get()); position.y_ += (heightfield->minHeight_ + heightfield->maxHeight_) * 0.5f; } Vector3 worldPosition(worldTransform * position); Quaternion worldRotation(worldTransform.Rotation() * rotation_); btDiscreteDynamicsWorld* world = physicsWorld_->GetWorld(); world->debugDrawObject(btTransform(ToBtQuaternion(worldRotation), ToBtVector3(worldPosition)), shape_, bodyActive ? WHITE : GREEN); physicsWorld_->SetDebugRenderer(0); } }
void Constraint::OnSetAttribute(const AttributeInfo& attr, const Variant& src) { Serializable::OnSetAttribute(attr, src); if (!attr.accessor_) { // Convenience for editing static constraints: if not connected to another body, adjust world position to match local // (when deserializing, the proper other body position will be read after own position, so this calculation is safely // overridden and does not accumulate constraint error if (attr.offset_ == offsetof(Constraint, position_) && constraint_ && !otherBody_) { btTransform ownBody = constraint_->getRigidBodyA().getWorldTransform(); btVector3 worldPos = ownBody * ToBtVector3(position_ * cachedWorldScale_ - ownBody_->GetCenterOfMass()); otherPosition_ = ToVector3(worldPos); } // Certain attribute changes require recreation of the constraint if (attr.offset_ == offsetof(Constraint, constraintType_) || attr.offset_ == offsetof(Constraint, otherBodyNodeID_) || attr.offset_ == offsetof(Constraint, disableCollision_)) recreateConstraint_ = true; else framesDirty_ = true; } }
void Constraint::CreateConstraint() { PROFILE(CreateConstraint); cachedWorldScale_ = node_->GetWorldScale(); ReleaseConstraint(); ownBody_ = GetComponent<RigidBody>(); btRigidBody* ownBody = ownBody_ ? ownBody_->GetBody() : 0; btRigidBody* otherBody = otherBody_ ? otherBody_->GetBody() : 0; // If no physics world available now mark for retry later if (!physicsWorld_ || !ownBody) { retryCreation_ = true; return; } if (!otherBody) otherBody = &btTypedConstraint::getFixedBody(); Vector3 ownBodyScaledPosition = position_ * cachedWorldScale_ - ownBody_->GetCenterOfMass(); Vector3 otherBodyScaledPosition = otherBody_ ? otherPosition_ * otherBody_->GetNode()->GetWorldScale() - otherBody_->GetCenterOfMass() : otherPosition_; switch (constraintType_) { case CONSTRAINT_POINT: { constraint_ = new btPoint2PointConstraint(*ownBody, *otherBody, ToBtVector3(ownBodyScaledPosition), ToBtVector3(otherBodyScaledPosition)); } break; case CONSTRAINT_HINGE: { btTransform ownFrame(ToBtQuaternion(rotation_), ToBtVector3(ownBodyScaledPosition)); btTransform otherFrame(ToBtQuaternion(otherRotation_), ToBtVector3(otherBodyScaledPosition)); constraint_ = new btHingeConstraint(*ownBody, *otherBody, ownFrame, otherFrame); } break; case CONSTRAINT_SLIDER: { btTransform ownFrame(ToBtQuaternion(rotation_), ToBtVector3(ownBodyScaledPosition)); btTransform otherFrame(ToBtQuaternion(otherRotation_), ToBtVector3(otherBodyScaledPosition)); constraint_ = new btSliderConstraint(*ownBody, *otherBody, ownFrame, otherFrame, false); } break; case CONSTRAINT_CONETWIST: { btTransform ownFrame(ToBtQuaternion(rotation_), ToBtVector3(ownBodyScaledPosition)); btTransform otherFrame(ToBtQuaternion(otherRotation_), ToBtVector3(otherBodyScaledPosition)); constraint_ = new btConeTwistConstraint(*ownBody, *otherBody, ownFrame, otherFrame); } break; default: break; } if (constraint_) { constraint_->setUserConstraintPtr(this); constraint_->setEnabled(IsEnabledEffective()); ownBody_->AddConstraint(this); if (otherBody_) otherBody_->AddConstraint(this); ApplyLimits(); physicsWorld_->GetWorld()->addConstraint(constraint_, disableCollision_); } recreateConstraint_ = false; framesDirty_ = false; retryCreation_ = false; }
void CollisionShape::UpdateShape() { PROFILE(UpdateCollisionShape); ReleaseShape(); if (!physicsWorld_) return; if (node_) { Vector3 newWorldScale = node_->GetWorldScale(); switch (shapeType_) { case SHAPE_BOX: shape_ = new btBoxShape(ToBtVector3(size_ * 0.5f)); shape_->setLocalScaling(ToBtVector3(newWorldScale)); break; case SHAPE_SPHERE: shape_ = new btSphereShape(size_.x_ * 0.5f); shape_->setLocalScaling(ToBtVector3(newWorldScale)); break; case SHAPE_STATICPLANE: shape_ = new btStaticPlaneShape(btVector3(0.0f, 1.0f, 0.0f), 0.0f); break; case SHAPE_CYLINDER: shape_ = new btCylinderShape(btVector3(size_.x_ * 0.5f, size_.y_ * 0.5f, size_.x_ * 0.5f)); shape_->setLocalScaling(ToBtVector3(newWorldScale)); break; case SHAPE_CAPSULE: shape_ = new btCapsuleShape(size_.x_ * 0.5f, Max(size_.y_ - size_.x_, 0.0f)); shape_->setLocalScaling(ToBtVector3(newWorldScale)); break; case SHAPE_CONE: shape_ = new btConeShape(size_.x_ * 0.5f, size_.y_); shape_->setLocalScaling(ToBtVector3(newWorldScale)); break; case SHAPE_TRIANGLEMESH: size_ = size_.Abs(); if (model_) { // Check the geometry cache Pair<Model*, unsigned> id = MakePair(model_.Get(), lodLevel_); HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& cache = physicsWorld_->GetTriMeshCache(); HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >::Iterator j = cache.Find(id); if (j != cache.End()) geometry_ = j->second_; else { geometry_ = new TriangleMeshData(model_, lodLevel_); // Check if model has dynamic buffers, do not cache in that case if (!HasDynamicBuffers(model_, lodLevel_)) cache[id] = geometry_; } TriangleMeshData* triMesh = static_cast<TriangleMeshData*>(geometry_.Get()); shape_ = new btScaledBvhTriangleMeshShape(triMesh->shape_, ToBtVector3(newWorldScale * size_)); // Watch for live reloads of the collision model to reload the geometry if necessary SubscribeToEvent(model_, E_RELOADFINISHED, HANDLER(CollisionShape, HandleModelReloadFinished)); } break; case SHAPE_CONVEXHULL: size_ = size_.Abs(); if (customGeometryID_ && GetScene()) { Node* node = GetScene()->GetNode(customGeometryID_); CustomGeometry* custom = node ? node->GetComponent<CustomGeometry>() : 0; if (custom) { geometry_ = new ConvexData(custom); ConvexData* convex = static_cast<ConvexData*>(geometry_.Get()); shape_ = new btConvexHullShape((btScalar*)convex->vertexData_.Get(), convex->vertexCount_, sizeof(Vector3)); shape_->setLocalScaling(ToBtVector3(newWorldScale * size_)); LOGINFO("Set convexhull from customgeometry"); } else LOGWARNING("Could not find custom geometry component from node ID " + String(customGeometryID_) + " for convex shape creation"); } else if (model_) { // Check the geometry cache Pair<Model*, unsigned> id = MakePair(model_.Get(), lodLevel_); HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >& cache = physicsWorld_->GetConvexCache(); HashMap<Pair<Model*, unsigned>, SharedPtr<CollisionGeometryData> >::Iterator j = cache.Find(id); if (j != cache.End()) geometry_ = j->second_; else { geometry_ = new ConvexData(model_, lodLevel_); // Check if model has dynamic buffers, do not cache in that case if (!HasDynamicBuffers(model_, lodLevel_)) cache[id] = geometry_; } ConvexData* convex = static_cast<ConvexData*>(geometry_.Get()); shape_ = new btConvexHullShape((btScalar*)convex->vertexData_.Get(), convex->vertexCount_, sizeof(Vector3)); shape_->setLocalScaling(ToBtVector3(newWorldScale * size_)); SubscribeToEvent(model_, E_RELOADFINISHED, HANDLER(CollisionShape, HandleModelReloadFinished)); } break; case SHAPE_TERRAIN: size_ = size_.Abs(); { Terrain* terrain = GetComponent<Terrain>(); if (terrain && terrain->GetHeightData()) { geometry_ = new HeightfieldData(terrain); HeightfieldData* heightfield = static_cast<HeightfieldData*>(geometry_.Get()); shape_ = new btHeightfieldTerrainShape(heightfield->size_.x_, heightfield->size_.y_, heightfield->heightData_.Get(), 1.0f, heightfield->minHeight_, heightfield->maxHeight_, 1, PHY_FLOAT, false); shape_->setLocalScaling(ToBtVector3(Vector3(heightfield->spacing_.x_, 1.0f, heightfield->spacing_.z_) * newWorldScale * size_)); } } break; default: break; } if (shape_) { shape_->setUserPointer(this); shape_->setMargin(margin_); } cachedWorldScale_ = newWorldScale; } if (physicsWorld_) physicsWorld_->CleanupGeometryCache(); recreateShape_ = false; }
PintJointHandle Bullet::CreateJoint(const PINT_JOINT_CREATE& desc) { ASSERT(mDynamicsWorld); btRigidBody* body0 = (btRigidBody*)desc.mObject0; btRigidBody* body1 = (btRigidBody*)desc.mObject1; btTypedConstraint* constraint = null; switch(desc.mType) { case PINT_JOINT_SPHERICAL: { const PINT_SPHERICAL_JOINT_CREATE& jc = static_cast<const PINT_SPHERICAL_JOINT_CREATE&>(desc); constraint = new btPoint2PointConstraint(*body0, *body1, ToBtVector3(jc.mLocalPivot0), ToBtVector3(jc.mLocalPivot1)); ASSERT(constraint); } break; case PINT_JOINT_HINGE: { const PINT_HINGE_JOINT_CREATE& jc = static_cast<const PINT_HINGE_JOINT_CREATE&>(desc); if(1) { ASSERT(jc.mGlobalAnchor.IsNotUsed()); ASSERT(jc.mGlobalAxis.IsNotUsed()); // const btTransform frameInA = CreateFrame(jc.mLocalPivot0, jc.mLocalAxis0); // const btTransform frameInB = CreateFrame(jc.mLocalPivot1, jc.mLocalAxis1); // btHingeConstraint* hc = new btHingeConstraint(*body0, *body1, frameInA, frameInB, false); btHingeConstraint* hc = new btHingeConstraint( *body0, *body1, ToBtVector3(jc.mLocalPivot0), ToBtVector3(jc.mLocalPivot1), ToBtVector3(jc.mLocalAxis0), ToBtVector3(jc.mLocalAxis1)); ASSERT(hc); constraint = hc; // float targetVelocity = 1.f; // float maxMotorImpulse = 1.0f; // hinge->enableAngularMotor(true,targetVelocity,maxMotorImpulse); if(jc.mMinLimitAngle!=MIN_FLOAT || jc.mMaxLimitAngle!=MAX_FLOAT) hc->setLimit(jc.mMinLimitAngle, jc.mMaxLimitAngle); // hc->setLimit(0.0f, 0.0f, 1.0f); } else { const btTransform frameInA = CreateFrame(jc.mLocalPivot0, jc.mLocalAxis0); const btTransform frameInB = CreateFrame(jc.mLocalPivot1, jc.mLocalAxis1); btGeneric6DofConstraint* hc = new btGeneric6DofConstraint(*body0, *body1, frameInA, frameInB, false); ASSERT(hc); constraint = hc; // hc->setAngularLowerLimit(btVector3(jc.mMinLimitAngle,0,0)); // hc->setAngularUpperLimit(btVector3(jc.mMaxLimitAngle,0,0)); /* btVector3 lowerSliderLimit = btVector3(-20,0,0); btVector3 hiSliderLimit = btVector3(-10,0,0); // btVector3 lowerSliderLimit = btVector3(-20,-5,-5); // btVector3 hiSliderLimit = btVector3(-10,5,5); spSlider1->setLinearLowerLimit(lowerSliderLimit); spSlider1->setLinearUpperLimit(hiSliderLimit); spSlider1->setAngularLowerLimit(btVector3(0,0,0)); spSlider1->setAngularUpperLimit(btVector3(0,0,0));*/ } } break; case PINT_JOINT_FIXED: { const PINT_FIXED_JOINT_CREATE& jc = static_cast<const PINT_FIXED_JOINT_CREATE&>(desc); if(1) // Emulating fixed joint with limited hinge { const Point LocalAxis(1,0,0); btHingeConstraint* hc = new btHingeConstraint( *body0, *body1, ToBtVector3(jc.mLocalPivot0), ToBtVector3(jc.mLocalPivot1), ToBtVector3(LocalAxis), ToBtVector3(LocalAxis)); ASSERT(hc); hc->setLimit(0.0f, 0.0f, 1.0f); constraint = hc; } } break; case PINT_JOINT_PRISMATIC: { const PINT_PRISMATIC_JOINT_CREATE& jc = static_cast<const PINT_PRISMATIC_JOINT_CREATE&>(desc); const btTransform frameInA = CreateFrame(jc.mLocalPivot0, jc.mLocalAxis0); const btTransform frameInB = CreateFrame(jc.mLocalPivot1, jc.mLocalAxis1); btSliderConstraint* sc = new btSliderConstraint( *body0, *body1, frameInA, frameInB, false); // sc->setUpperLinLimit(10.0f); // sc->setLowerLinLimit(-10.0f); constraint = sc; } break; } if(constraint) mDynamicsWorld->addConstraint(constraint, true); return constraint; }
PintObjectHandle Bullet::CreateObject(const PINT_OBJECT_CREATE& desc) { udword NbShapes = desc.GetNbShapes(); if(!NbShapes) return null; ASSERT(mDynamicsWorld); const PINT_SHAPE_CREATE* CurrentShape = desc.mShapes; float FrictionCoeff = 0.5f; float RestitutionCoeff = 0.0f; btCollisionShape* colShape = null; if(NbShapes>1) { btCompoundShape* CompoundShape = new btCompoundShape(); colShape = CompoundShape; mCollisionShapes.push_back(colShape); while(CurrentShape) { if(CurrentShape->mMaterial) { FrictionCoeff = CurrentShape->mMaterial->mDynamicFriction; RestitutionCoeff = CurrentShape->mMaterial->mRestitution; } const btTransform LocalPose(ToBtQuaternion(CurrentShape->mLocalRot), ToBtVector3(CurrentShape->mLocalPos)); // ### TODO: where is this deleted? btCollisionShape* subShape = CreateBulletShape(*CurrentShape); if(subShape) { CompoundShape->addChildShape(LocalPose, subShape); } CurrentShape = CurrentShape->mNext; } } else { colShape = CreateBulletShape(*CurrentShape); if(CurrentShape->mMaterial) { FrictionCoeff = CurrentShape->mMaterial->mDynamicFriction; RestitutionCoeff = CurrentShape->mMaterial->mRestitution; } } const bool isDynamic = (desc.mMass != 0.0f); btVector3 localInertia(0,0,0); if(isDynamic) colShape->calculateLocalInertia(desc.mMass, localInertia); const btTransform startTransform(ToBtQuaternion(desc.mRotation), ToBtVector3(desc.mPosition)); //using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform); btRigidBody::btRigidBodyConstructionInfo rbInfo(desc.mMass, myMotionState, colShape, localInertia); { rbInfo.m_friction = FrictionCoeff; rbInfo.m_restitution = RestitutionCoeff; // rbInfo.m_startWorldTransform; rbInfo.m_linearDamping = gLinearDamping; rbInfo.m_angularDamping = gAngularDamping; if(!gEnableSleeping) { // rbInfo.m_linearSleepingThreshold = 99999999.0f; // rbInfo.m_angularSleepingThreshold = 99999999.0f; // rbInfo.m_linearSleepingThreshold = 0.0f; // rbInfo.m_angularSleepingThreshold = 0.0f; } // rbInfo.m_additionalDamping; // rbInfo.m_additionalDampingFactor; // rbInfo.m_additionalLinearDampingThresholdSqr; // rbInfo.m_additionalAngularDampingThresholdSqr; // rbInfo.m_additionalAngularDampingFactor; } btRigidBody* body = new btRigidBody(rbInfo); ASSERT(body); if(!gEnableSleeping) body->setActivationState(DISABLE_DEACTIVATION); sword collisionFilterGroup, collisionFilterMask; if(isDynamic) { body->setLinearVelocity(ToBtVector3(desc.mLinearVelocity)); body->setAngularVelocity(ToBtVector3(desc.mAngularVelocity)); collisionFilterGroup = short(btBroadphaseProxy::DefaultFilter); collisionFilterMask = short(btBroadphaseProxy::AllFilter); if(desc.mCollisionGroup) { const udword btGroup = RemapCollisionGroup(desc.mCollisionGroup); ASSERT(btGroup<32); collisionFilterGroup = short(1<<btGroup); collisionFilterMask = short(mGroupMasks[btGroup]); } } else { // body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT); body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT|btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); collisionFilterGroup = short(btBroadphaseProxy::StaticFilter); collisionFilterMask = short(btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter); } if(desc.mAddToWorld) // mDynamicsWorld->addRigidBody(body); mDynamicsWorld->addRigidBody(body, collisionFilterGroup, collisionFilterMask); if(gUseCCD) { // body->setCcdMotionThreshold(1e-7); // body->setCcdSweptSphereRadius(0.9*CUBE_HALF_EXTENTS); body->setCcdMotionThreshold(0.0001f); body->setCcdSweptSphereRadius(0.4f); } return body; }
void Bullet::SetGravity(const Point& gravity) { ASSERT(mDynamicsWorld); mDynamicsWorld->setGravity(ToBtVector3(gravity)); }
void Bullet::Init(const PINT_WORLD_CREATE& desc) { for(udword i=0;i<32;i++) mGroupMasks[i] = 0xffffffff; gNbAllocs = 0; gCurrentMemory = 0; if(gUseCustomMemoryAllocator) { btAlignedAllocSetCustomAligned(__btAlignedAllocFunc, __btAlignedFreeFunc); btAlignedAllocSetCustom(__btAllocFunc, __btFreeFunc); } ///collision configuration contains default setup for memory, collision setup. Advanced users can create their own configuration. btDefaultCollisionConstructionInfo cci; // cci.m_defaultMaxPersistentManifoldPoolSize = 32768; cci.m_defaultMaxPersistentManifoldPoolSize = 32; mCollisionConfiguration = new btDefaultCollisionConfiguration(cci); ///use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded) mDispatcher = new btCollisionDispatcher(mCollisionConfiguration); // mDispatcher->setDispatcherFlags(btCollisionDispatcher::CD_DISABLE_CONTACTPOOL_DYNAMIC_ALLOCATION); ///btDbvtBroadphase is a good general purpose broadphase. You can also try out btAxis3Sweep. if(gUseDbvt) { mBroadPhase = new btDbvtBroadphase(); } else { if(desc.mGlobalBounds.IsValid()) { Point m, M; desc.mGlobalBounds.GetMin(m); desc.mGlobalBounds.GetMax(M); mBroadPhase = new btAxisSweep3(ToBtVector3(m), ToBtVector3(M)); } else { mBroadPhase = new btAxisSweep3(btVector3(-gGlobalBoxSize, -gGlobalBoxSize, -gGlobalBoxSize), btVector3(gGlobalBoxSize, gGlobalBoxSize, gGlobalBoxSize)); } } ///the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded) mSolver = new btSequentialImpulseConstraintSolver; // mSolver = new btOdeQuickstepConstraintSolver(); mDynamicsWorld = new btDiscreteDynamicsWorld(mDispatcher, mBroadPhase, mSolver, mCollisionConfiguration); mDynamicsWorld->setGravity(ToBtVector3(desc.mGravity)); btContactSolverInfo& SolverInfo = mDynamicsWorld->getSolverInfo(); if(gEnableFrictionDirCaching) SolverInfo.m_solverMode |= SOLVER_ENABLE_FRICTION_DIRECTION_CACHING; //don't recalculate friction values each frame else SolverInfo.m_solverMode &= ~SOLVER_ENABLE_FRICTION_DIRECTION_CACHING; if(gRandomizeOrder) SolverInfo.m_solverMode |= SOLVER_RANDMIZE_ORDER; else SolverInfo.m_solverMode &= ~SOLVER_RANDMIZE_ORDER; if(gWarmStarting) SolverInfo.m_solverMode |= SOLVER_USE_WARMSTARTING; else SolverInfo.m_solverMode &= ~SOLVER_USE_WARMSTARTING; SolverInfo.m_numIterations = gSolverIterationCount; SolverInfo.m_splitImpulse = gUseSplitImpulse; SolverInfo.m_erp = gErp; SolverInfo.m_erp2 = gErp2; /* discreteWorld->getSolverInfo().m_linearSlop = gSlop; discreteWorld->getSolverInfo().m_warmstartingFactor = gWarmStartingParameter; */ mDynamicsWorld->getDispatchInfo().m_useContinuous = gUseCCD; mDynamicsWorld->setDebugDrawer(&gDebugDrawer); mDynamicsWorld->setForceUpdateAllAabbs(false); gContactAddedCallback = CustomMaterialCombinerCallback; }
Vector3 RigidBody::GetVelocityAtPoint(const Vector3& position) const { return body_ ? ToVector3(body_->getVelocityInLocalPoint(ToBtVector3(position - centerOfMass_))) : Vector3::ZERO; }