/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "physic_bullet/bullet_item.h" #include "physic_bullet/bullet_world.h" #include "BulletCollision/CollisionDispatch/btGhostObject.h" #include "physic_bullet/bullet_character_controller.h" #include "physic/physic_item_desc.h" #include "core/item.h" #include "core/terrain.h" #include "scene3d/mitem.h" #include "math/matrix4.h" #include "log/log.h" using namespace GS; using namespace GS::S3D; //------------------------------------------------------------------------------ static Vector4 btTonVector(const btVector3 &v) { return Vector4(v.x(), v.y(), v.z()); } static btVector3 nTobtVector(const Vector4 &v) { return btVector3(v.x, v.y, v.z); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletMotionState::setWorldTransform(const btTransform &comt) { btTransform wt = comt; const Vector4 &com = item->GetCenter(); wt.setOrigin(wt.getOrigin() - wt.getBasis() * btVector3(com.x, com.y, com.z)); item->TransformToMatrix4(wt, bullet_matrix); bullet_matrix = bullet_matrix * Matrix4::ScaleMatrix(item->GetScale()); } void BulletMotionState::getWorldTransform(btTransform &wt) const { item->TransformFromMatrix4(engine_matrix, wt); const Vector4 &com = item->GetCenter(); wt.setOrigin(wt.getOrigin() + wt.getBasis() * btVector3(com.x, com.y, com.z)); } BulletMotionState::BulletMotionState(BulletPhysicItem *_item) : item(_item) { MItem *mitem = (MItem *)_item->GetUserPointer(); bullet_matrix = mitem->GetBaseItem()->GetMatrix(); engine_matrix = bullet_matrix; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint BulletPhysicItem::GetSelfMask() const { btBroadphaseProxy *handle = NULL; if (rigid_body) handle = rigid_body->getBroadphaseHandle(); if (ghost_object) handle = ghost_object->getBroadphaseHandle(); return handle ? handle->m_collisionFilterGroup : 0; } void BulletPhysicItem::SetSelfMask(uint m) { self_mask = m; btBroadphaseProxy *handle = NULL; if (rigid_body) handle = rigid_body->getBroadphaseHandle(); if (ghost_object) handle = ghost_object->getBroadphaseHandle(); if (handle) { handle->m_collisionFilterGroup = (short)m; // Refresh pair cache. btworld->getBroadphase()->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(handle, btworld->getDispatcher()); } } uint BulletPhysicItem::GetCollisionMask() const { btBroadphaseProxy *handle = NULL; if (rigid_body) handle = rigid_body->getBroadphaseHandle(); if (ghost_object) handle = ghost_object->getBroadphaseHandle(); return handle ? handle->m_collisionFilterMask : 0; } void BulletPhysicItem::SetCollisionMask(uint m) { collision_mask = m; btBroadphaseProxy *handle = NULL; if (rigid_body) handle = rigid_body->getBroadphaseHandle(); if (ghost_object) handle = ghost_object->getBroadphaseHandle(); if (handle) { handle->m_collisionFilterMask = (short)m; // Refresh pair cache. btworld->getBroadphase()->getOverlappingPairCache()->removeOverlappingPairsContainingProxy(handle, btworld->getDispatcher()); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::SetLinearDamping(float k) { if (rigid_body) rigid_body->setDamping(1.f - k, rigid_body->getAngularDamping()); } float BulletPhysicItem::GetLinearDamping() const { return rigid_body ? 1.f - rigid_body->getLinearDamping() : 0; } void BulletPhysicItem::SetAngularDamping(float k) { if (rigid_body) rigid_body->setDamping(rigid_body->getLinearDamping(), 1.f - k); } float BulletPhysicItem::GetAngularDamping() const { return rigid_body ? 1.f - rigid_body->getAngularDamping() : 0; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::SetLinearFactor(const Vector4 &k) { if (rigid_body) rigid_body->setLinearFactor(btVector3(k.x, k.y, k.z)); } void BulletPhysicItem::SetAngularFactor(const Vector4 &k) { if (rigid_body) rigid_body->setAngularFactor(btVector3(k.x, k.y, k.z)); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::TransformFromMatrix4(const Matrix4 &m, btTransform &transform) { btScalar scalar[15]; scalar[0] = m.m[0][0]; scalar[1] = m.m[1][0]; scalar[2] = m.m[2][0]; scalar[3] = 1; scalar[4] = m.m[0][1]; scalar[5] = m.m[1][1]; scalar[6] = m.m[2][1]; scalar[7] = 1; scalar[8] = m.m[0][2]; scalar[9] = m.m[1][2]; scalar[10] = m.m[2][2]; scalar[11] = 1; scalar[12] = m.m[0][3]; scalar[13] = m.m[1][3]; scalar[14] = m.m[2][3]; transform.setFromOpenGLMatrix(scalar); } void BulletPhysicItem::TransformToMatrix4(const btTransform &transform, Matrix4 &m) { btScalar scalar[16]; transform.getOpenGLMatrix(scalar); m.m[0][0] = scalar[0]; m.m[1][0] = scalar[1]; m.m[2][0] = scalar[2]; m.m[3][0] = 0; m.m[0][1] = scalar[4]; m.m[1][1] = scalar[5]; m.m[2][1] = scalar[6]; m.m[3][1] = 0; m.m[0][2] = scalar[8]; m.m[1][2] = scalar[9]; m.m[2][2] = scalar[10]; m.m[3][2] = 0; m.m[0][3] = scalar[12]; m.m[1][3] = scalar[13]; m.m[2][3] = scalar[14]; m.m[3][3] = scalar[15]; } void BulletPhysicItem::GetGraphicMatrix(Matrix4 &m) { if (ghost_object) { btTransform wt = ghost_object->getWorldTransform(); wt.setOrigin(wt.getOrigin() - wt.getBasis() * btVector3(center.x, center.y, center.z)); TransformToMatrix4(wt, m); } else if (motion_state) m = motion_state->GetGraphicMatrix(); } void BulletPhysicItem::SetEngineMatrix(const Matrix4 &m) { if (motion_state) motion_state->SetEngineMatrix(m); } void BulletPhysicItem::GetMatrix(Matrix4 &m) { if (ghost_object) TransformToMatrix4(ghost_object->getWorldTransform(), m); else if (rigid_body) TransformToMatrix4(rigid_body->getWorldTransform(), m); } void BulletPhysicItem::SetMatrix(const Matrix4 &m) { Vector4 p; Matrix3 m3; m.Decompose(&p, 0, &m3); Matrix4 m4(Matrix4::FromMatrix3(m3)); m4.SetRow(3, p); btTransform wt; TransformFromMatrix4(m4, wt); wt.setOrigin(wt.getOrigin() + wt.getBasis() * btVector3(center.x, center.y, center.z)); if (ghost_object) ghost_object->setWorldTransform(wt); else if (rigid_body) rigid_body->setWorldTransform(wt); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::SetSleeping(bool sleep) { if (!rigid_body) return; if (sleep) rigid_body->setActivationState(WANTS_DEACTIVATION); else rigid_body->activate(); } bool BulletPhysicItem::IsSleeping() const { return rigid_body ? rigid_body->wantsSleeping() : false; } void BulletPhysicItem::SetActive(bool active) { if (!rigid_body) return; if (active) { /* Note the activation state MUST be changed from DISABLE_SIMULATION or activate() will silently fail. */ rigid_body->getBroadphaseHandle()->m_collisionFilterGroup = self_mask; rigid_body->getBroadphaseHandle()->m_collisionFilterMask = collision_mask; rigid_body->forceActivationState(ACTIVE_TAG); rigid_body->activate(); } else { rigid_body->setActivationState(DISABLE_SIMULATION); rigid_body->getBroadphaseHandle()->m_collisionFilterGroup = 0; rigid_body->getBroadphaseHandle()->m_collisionFilterMask = 0; } } bool BulletPhysicItem::GetActive() const { return rigid_body ? rigid_body->isActive() : false; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::VehicleSetForce(float F, uint i) { if (vehicle && (i < (uint)vehicle->getNumWheels())) vehicle->applyEngineForce(F, i); } void BulletPhysicItem::VehicleSetBrake(float F, uint i) { if (vehicle && (i < (uint)vehicle->getNumWheels())) vehicle->setBrake(F, i); } void BulletPhysicItem::VehicleSetSteering(float v, uint i) { if (vehicle && (i < (uint)vehicle->getNumWheels())) vehicle->setSteeringValue(v, i); } void BulletPhysicItem::VehicleSetFriction(float f, uint i) { if (vehicle && (i < (uint)vehicle->getNumWheels())) vehicle->getWheelInfo(i).m_frictionSlip = f; } Matrix4 BulletPhysicItem::VehicleGetWheelMatrix(uint i) { Matrix4 m(Matrix4::IdentityMatrix()); if (vehicle && (i < (uint)vehicle->getNumWheels())) { vehicle->updateWheelTransform(i, true); TransformToMatrix4(vehicle->getWheelInfo(i).m_worldTransform, m); } return m; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::CharacterSetRotationMatrix(const Matrix3 &m) { if (ghost_object) { btMatrix3x3 basis ( m.m[0][0], m.m[0][1], m.m[0][2], m.m[1][0], m.m[1][1], m.m[1][2], m.m[2][0], m.m[2][1], m.m[2][2] ); ghost_object->getWorldTransform().setBasis(basis); } } void BulletPhysicItem::CharacterSetVelocity(const Vector4 &v) { if (character_controller) character_controller->setWalkDirection(nTobtVector(v)); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::SetScale(const Vector4 &_scale) { scale = _scale; if (compound) compound->setLocalScaling(nTobtVector(scale)); } Vector4 BulletPhysicItem::GetScale() const { return scale; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::ResetBody() { if (rigid_body) { rigid_body->setLinearVelocity(btVector3(0, 0, 0)); rigid_body->setAngularVelocity(btVector3(0, 0, 0)); rigid_body->clearForces(); } } void BulletPhysicItem::ForceUpdateMassShapePhysic(const PhysicItemDesc &desc, PhysicWorld *world) { float total_mass = 0; if (!desc.shape_list.GetCount()) return; Array mass_array; mass_array.Allocate(desc.shape_list.GetCount()); center.Set(0, 0, 0); btScalar *pmass_array = mass_array.c_ptr(); ListForeachPtr(PhysicShape *, shape, desc.shape_list) { Vector4 shape_center = shape->position; center += shape_center * shape->mass; total_mass += shape->mass; *pmass_array++ = shape->mass; } center /= total_mass; btTransform principal; btVector3 body_inertia(0, 0, 0); compound->calculatePrincipalAxisTransform(mass_array, principal, body_inertia); rigid_body->setMassProps(total_mass, body_inertia); rigid_body->updateInertiaTensor(); } //------------------------------------------------------------------------------ float BulletPhysicItem::SetupCollisionShapes(const PhysicItemDesc &desc, Array &mass_array, PhysicWorld *world) { float total_mass = 0; if (!desc.shape_list.GetCount()) return total_mass; mass_array.Allocate(desc.shape_list.GetCount()); btScalar *pmass_array = mass_array.c_ptr(); // WTF man... can't Bullet handle COM offset by itself? shapes.Allocate(desc.shape_list.GetCount()); center.Set(0, 0, 0); uint n = 0; ListForeachPtr(PhysicShape *, shape, desc.shape_list) { btCollisionShape *btshape = NULL; Vector4 shape_center = shape->position; switch (shape->GetType()) { case PhysicShape::TypeNone: break; case PhysicShape::TypeHeightmap: if (float *ph = shape->GetHeightmap()) { float min, max; min = max = ph[0]; for (int v = 0; v < shape->GetHeight(); ++v) for (int u = 0; u < shape->GetWidth(); ++u) { if (ph[0] > max) max = ph[0]; if (ph[0] < min) min = ph[0]; ph++; } btshape = new btHeightfieldTerrainShape(shape->GetWidth(), shape->GetHeight(), (void *)shape->GetHeightmap(), 1.f, min, max, 1, PHY_FLOAT, false); } break; case PhysicShape::TypeSphere: btshape = new btSphereShape(shape->dimensions.x); break; case PhysicShape::TypeBox: { const Vector4 &d = shape->dimensions; btshape = new btBoxShape(btVector3(d.x * 0.5f, d.y * 0.5f, d.z * 0.5f)); } break; case PhysicShape::TypeCapsule: { const Vector4 &d = shape->dimensions; btshape = new btCapsuleShapeZ(d.x * 0.5f, d.z); } break; case PhysicShape::TypeCylinder: { const Vector4 &d = shape->dimensions; btshape = new btCylinderShapeZ(btVector3(d.x * 0.5f, d.y * 0.5f, d.z * 0.5f)); } break; case PhysicShape::TypeCone: { const Vector4 &d = shape->dimensions; btshape = new btConeShapeZ(d.x * 0.5f, d.z * 0.5f); } break; case PhysicShape::TypeConvex: if (BulletConvex *convex = ((BulletWorld *)world)->LoadConvex(shape->path)) { shapes[n].convex = convex; shape_center = convex->center * shape->GetMatrix(); btshape = convex->convex; } break; case PhysicShape::TypeMesh: if (desc.physic_mode == PhysicItemDesc::Mode_Static) { /* [EJ] 06/03/13 - Bullet SILENTLY rescales the cached mesh vertices to comply with the item scale. In order to support multiple scales on the same mesh the path is suffixed with the item scale. */ String suffix = String::Format("%.02f_%.02f_%.02f", scale.x, scale.y, scale.z); if (BulletMesh *mesh = ((BulletWorld *)world)->LoadMesh(shape->path, suffix)) { shapes[n].mesh = mesh; shape_center = mesh->center * shape->GetMatrix(); btshape = mesh->mesh; } } break; } shapes[n].shape = btshape; center += shape_center * shape->mass; total_mass += shape->mass; *pmass_array++ = shape->mass; ++n; } center /= total_mass; if (desc.physic_mode == PhysicItemDesc::Mode_Vehicle) center.Set(0, 0, 0); n = 0; ListForeachPtr(PhysicShape *, shape, desc.shape_list) { if (btCollisionShape *btshape = shapes[n].shape) { Vector4 shape_offset(0, 0, 0); if (shape->GetType() == PhysicShape::TypeHeightmap) { float *ph = shape->GetHeightmap(); float min, max; min = max = ph[0]; for (int v = 0; v < shape->GetHeight(); ++v) for (int u = 0; u < shape->GetWidth(); ++u) { if (ph[0] > max) max = ph[0]; if (ph[0] < min) min = ph[0]; ph++; } btshape->setLocalScaling(btVector3(1, 1, 1)); // Damn... what a mess. float hy = (max - min) * -0.5f; shape_offset.Set(0, min - hy, 0); } Matrix4 m = Matrix4::TransformationMatrix(shape->position + shape_offset - center, shape->rotation, shape->scale); btTransform transform; TransformFromMatrix4(m, transform); compound->addChildShape(transform, btshape); } ++n; } return total_mass; } bool BulletPhysicItem::SetupCharacterController(const PhysicItemDesc &desc) { ghost_object = new btPairCachingGhostObject(); ghost_object->setUserPointer((PhysicItem *)this); #if 0 convex_shape = new btCylinderShape(btVector3(desc.character.radius, desc.character.height * 0.5f, desc.character.radius)); center.Set(0, desc.character.height * 0.5f, 0); #else float height = Types::Max(desc.character.height - desc.character.radius * 2.f, 0.f); convex_shape = new btCapsuleShape(desc.character.radius, height); // Height is the distance between the center of the two spheres whose convex hull is a capsule. center.Set(0, (height + desc.character.radius * 2.f) * 0.5f, 0); #endif ghost_object->setCollisionShape(convex_shape); ghost_object->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT); #if 1 character_controller = new btKinematicCharacterController(ghost_object, convex_shape, desc.character.max_step); #else btCustomCharacterController *cc = new btCustomCharacterController(ghost_object, convex_shape, desc.character.max_step, btworld->getCollisionWorld()); character_controller = cc; #endif return true; } bool BulletPhysicItem::SetupKinematicDynamicBody(const PhysicItemDesc &desc, PhysicWorld *world) { // Setup collision shapes. Array mass_array; compound = new btCompoundShape; float total_mass = SetupCollisionShapes(desc, mass_array, world); if (!compound->getNumChildShapes()) return true; // Initialize physic mode. compound->setLocalScaling(nTobtVector(scale)); btVector3 body_inertia(0, 0, 0); switch (desc.physic_mode) { case PhysicItemDesc::Mode_None: break; case PhysicItemDesc::Mode_Dynamic: case PhysicItemDesc::Mode_Vehicle: if (compound->getNumChildShapes()) { btTransform principal; compound->calculatePrincipalAxisTransform(mass_array, principal, body_inertia); } else { total_mass = 1; body_inertia.setValue(1, 1, 1); } break; case PhysicItemDesc::Mode_Static: case PhysicItemDesc::Mode_Kinematic: total_mass = 0; break; } // Allocate motion state. motion_state = new BulletMotionState(this); // Create rigid body. rigid_body = new btRigidBody(btRigidBody::btRigidBodyConstructionInfo(total_mass, motion_state, compound, body_inertia)); rigid_body->setUserPointer((PhysicItem *)this); // Vehicle specialization. if (desc.physic_mode == PhysicItemDesc::Mode_Vehicle) { rigid_body->setActivationState(DISABLE_DEACTIVATION); vehicle_raycaster = new btDefaultVehicleRaycaster(btworld); vehicle = new btRaycastVehicle(btRaycastVehicle::btVehicleTuning(), rigid_body, vehicle_raycaster); vehicle->setCoordinateSystem(0, 1, 2); // Add wheels. ListForeachPtr(PhysicWheel *, wheel, desc.vehicle.wheel_list) { btRaycastVehicle::btVehicleTuning tuning; tuning.m_suspensionStiffness = wheel->stiffness; tuning.m_suspensionDamping = wheel->damping; tuning.m_suspensionCompression = wheel->damping; tuning.m_frictionSlip = wheel->friction; tuning.m_maxSuspensionTravelCm = wheel->max_compression * 10.f; // m to cm. Vector4 o = wheel->ref_matrix.GetRow(3), u = wheel->ref_matrix.GetRow(1).Reversed(), l = wheel->ref_matrix.GetRow(0).Reversed(); vehicle->addWheel(btVector3(o.x, o.y, o.z), btVector3(u.x, u.y, u.z), btVector3(l.x, l.y, l.z), wheel->rest_length, wheel->radius > 0.01f ? wheel->radius : 0.01f, tuning, false); } } // Set defaults. SetLinearFactor(desc.linear_factor); SetAngularFactor(desc.angular_factor); SetLinearDamping(desc.linear_damping); SetAngularDamping(desc.angular_damping); if (desc.shape_list.GetCount()) { PhysicShape *shape = desc.shape_list.GetRoot()->Object(); rigid_body->setFriction(shape->static_friction); rigid_body->setRestitution(shape->restitution); } if (desc.physic_mode == PhysicItemDesc::Mode_Kinematic) { rigid_body->setCollisionFlags(rigid_body->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT); rigid_body->setActivationState(DISABLE_DEACTIVATION); } return true; } bool BulletPhysicItem::SetupBody(const PhysicItemDesc &desc, PhysicWorld *world) { // EJ 11/10 // // - Bullet constraint holds strong/unmanaged reference to Bullet rigid bodies. // - A Bullet rigid body is not meant to be radically modified once created. // BulletWorld *world = (PhysicWorld *)world; if (rigid_body || ghost_object) return true; DeleteBody(); switch (desc.physic_mode) { case PhysicItemDesc::Mode_None: return true; case PhysicItemDesc::Mode_Character: if (!SetupCharacterController(desc)) return false; btworld->addCollisionObject(ghost_object, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter); btworld->addAction(character_controller); break; default: if (!SetupKinematicDynamicBody(desc, world)) return false; if (rigid_body) { btworld->addRigidBody(rigid_body); const Vector4 &g(world->GetGravity()); rigid_body->setGravity(btVector3(g.x, g.y, g.z)); } if (vehicle) btworld->addVehicle(vehicle); break; } SetCollisionMask(desc.collision_mask); SetSelfMask(desc.self_mask); return true; } void BulletPhysicItem::DeleteBody() { // Destroy all shapes. if (compound) while (compound->getNumChildShapes()) compound->removeChildShapeByIndex(0); // The collision shape for mesh/convex are cached and should not be deleted here! for (uint n = 0; n < shapes.GetCount(); ++n) if (shapes[n].convex.IsValid() || shapes[n].mesh.IsValid()) shapes[n].shape.Detach(); shapes.Free(); // Destroy rigid body. if (rigid_body) btworld->removeRigidBody(rigid_body); rigid_body = NULL; if (vehicle) btworld->removeVehicle(vehicle); vehicle = NULL; vehicle_raycaster = NULL; if (ghost_object) btworld->removeCollisionObject(ghost_object); ghost_object = NULL; if (character_controller) btworld->removeAction(character_controller); character_controller = NULL; compound = NULL; motion_state = NULL; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void BulletPhysicItem::SetGravity(const Vector4 &g) { if (rigid_body) rigid_body->setGravity(btVector3(g.x, g.y, g.z)); } Vector4 BulletPhysicItem::GetGravity() const { if (!rigid_body) return Vector4(0, 0, 0); btVector3 g = rigid_body->getGravity(); return Vector4(g.x(), g.y(), g.z()); } void BulletPhysicItem::ApplyImpulse(const Vector4 &I, const Vector4 *p) { if (!rigid_body) return; SetSleeping(false); if (p && (I.Len() > 0.0001)) { btVector3 l(p->x, p->y, p->z); btVector3 J(I.x, I.y, I.z); btScalar k = rigid_body->computeImpulseDenominator(l, J.normalized()); rigid_body->applyImpulse(J / k, l - rigid_body->getCenterOfMassPosition()); } else { btVector3 J(I.x, I.y, I.z); rigid_body->applyCentralImpulse(J / rigid_body->getInvMass()); } } void BulletPhysicItem::ApplyForce(const Vector4 &F, const Vector4 *p) { if (!rigid_body) return; SetSleeping(false); if (p) rigid_body->applyForce(btVector3(F.x, F.y, F.z), btVector3(p->x, p->y, p->z) - rigid_body->getCenterOfMassPosition()); else rigid_body->applyCentralForce(btVector3(F.x, F.y, F.z)); } void BulletPhysicItem::ApplyTorque(const Vector4 &T) { if (rigid_body) rigid_body->applyTorque(rigid_body->getCenterOfMassTransform().getBasis() * btVector3(T.x, T.y, T.z)); } void BulletPhysicItem::SetAngularVelocity(const Vector4 &w) { if (rigid_body) rigid_body->setAngularVelocity(btVector3(w.x, w.y, w.z)); } Vector4 BulletPhysicItem::GetAngularVelocity() const { if (!rigid_body) return Vector4(0, 0, 0); const btVector3 &v = rigid_body->getAngularVelocity(); return Vector4(v.x(), v.y(), v.z()); } void BulletPhysicItem::SetLinearVelocity(const Vector4 &v) { if (rigid_body) rigid_body->setLinearVelocity(btVector3(v.x, v.y, v.z)); } Vector4 BulletPhysicItem::GetLinearVelocity() const { if (!rigid_body) return Vector4(0, 0, 0); const btVector3 &v = rigid_body->getLinearVelocity(); return Vector4(v.x(), v.y(), v.z()); } Vector4 BulletPhysicItem::GetLocalPointVelocity(const Vector4 &p) const { if (!rigid_body) return Vector4(0, 0, 0); btVector3 v = rigid_body->getVelocityInLocalPoint(rigid_body->getCenterOfMassTransform().getBasis() * btVector3(p.x, p.y, p.z)); return Vector4(v.x(), v.y(), v.z()); } Vector4 BulletPhysicItem::GetWorldPointVelocity(const Vector4 &wp) const { if (!rigid_body) return Vector4(0, 0, 0); btVector3 v = rigid_body->getVelocityInLocalPoint(btVector3(wp.x, wp.y, wp.z) - rigid_body->getCenterOfMassPosition()); return Vector4(v.x(), v.y(), v.z()); } Vector4 BulletPhysicItem::GetCenterOfMass() const { if (!rigid_body) return Vector4(0, 0, 0); btVector3 p = rigid_body->getCenterOfMassPosition(); return Vector4(p.x(), p.y(), p.z()); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ BulletPhysicItem::BulletPhysicItem(btDiscreteDynamicsWorld *w) { btworld = w; center.Set(0, 0, 0); scale.Set(1, 1, 1); } BulletPhysicItem::~BulletPhysicItem() { DeleteBody(); } //----------------------------------------------------------------------------------