BigRigDemo::BigRigDemo(hkDemoEnvironment* env) : CarDemo(env, false) { // Create the vehicles // // Create tractor shape. // hkpListShape* tractorShape = HK_NULL; { hkReal xSize = 1.9f; hkReal xSizeFrontTop = 0.7f; hkReal xSizeFrontMid = 1.6f; hkReal ySize = 0.9f; hkReal ySizeMid = ySize - 0.7f; hkReal zSize = 1.0f; //hkReal xBumper = 1.9f; //hkReal yBumper = 0.35f; //hkReal zBumper = 1.0f; hkVector4 halfExtents(1.0f, 0.35f, 1.0f); // 16 = 4 (size of "each float group", 3 for x,y,z, 1 for padding) * 4 (size of float). int stride = sizeof(float) * 4; { int numVertices = 10; float vertices[] = { xSizeFrontTop, ySize, zSize, 0.0f, // v0 xSizeFrontTop, ySize, -zSize, 0.0f, // v1 xSize, -ySize, zSize, 0.0f, // v2 xSize, -ySize, -zSize, 0.0f, // v3 -xSize, ySize, zSize, 0.0f, // v4 -xSize, ySize, -zSize, 0.0f, // v5 -xSize, -ySize, zSize, 0.0f, // v6 -xSize, -ySize, -zSize, 0.0f, // v7 xSizeFrontMid, ySizeMid, zSize, 0.0f, // v8 xSizeFrontMid, ySizeMid, -zSize, 0.0f, // v9 }; hkpBoxShape* boxShape = new hkpBoxShape(halfExtents, 0.05f ); hkTransform transform; transform.setIdentity(); transform.setTranslation(hkVector4(-2.5f, -0.55f, 0.0f)); hkpConvexTransformShape* transformShape = new hkpConvexTransformShape(boxShape, transform); boxShape->removeReference(); hkpConvexVerticesShape* convexShape; { hkStridedVertices stridedVerts; { stridedVerts.m_numVertices = numVertices; stridedVerts.m_striding = stride; stridedVerts.m_vertices = vertices; } convexShape = new hkpConvexVerticesShape(stridedVerts); } hkArray<hkpShape*> shapeArray; shapeArray.pushBack(transformShape); shapeArray.pushBack(convexShape); tractorShape = new hkpListShape(shapeArray.begin(), shapeArray.getSize()); transformShape->removeReference(); convexShape->removeReference(); } } // // Create trailer shape. // hkpListShape* trailerShape = HK_NULL; { hkVector4 halfExtents1(5.0f, 1.5f, 1.0f); // Box hkVector4 halfExtents2(1.0f, 1.0f, 1.0f); // Tongue hkTransform transform; transform.setIdentity(); { hkpBoxShape* boxShape1 = new hkpBoxShape(halfExtents1, 0.05f ); hkpBoxShape* boxShape2 = new hkpBoxShape(halfExtents2, 0.05f ); transform.setTranslation(hkVector4(6.0f, 0.5f, 0.0f)); hkpConvexTransformShape* transformShape = new hkpConvexTransformShape(boxShape2, transform); boxShape2->removeReference(); hkArray<hkpShape*> shapeArray; shapeArray.pushBack(boxShape1); shapeArray.pushBack(transformShape); trailerShape = new hkpListShape(shapeArray.begin(), shapeArray.getSize()); boxShape1->removeReference(); transformShape->removeReference(); } } // Create the tractor vehicles { m_world->lock(); { HK_ASSERT(0x526edf5d, m_world->getCollisionFilter() && m_world->getCollisionFilter()->m_type == hkpCollisionFilter::HK_FILTER_GROUP); hkpGroupFilter* groupFilter = static_cast<hkpGroupFilter*>(const_cast<hkpCollisionFilter*>(m_world->getCollisionFilter())); m_vehicles.clear(); for (int vehicleId = 0; vehicleId < m_numVehicles; vehicleId++) { int vehicleGroup = groupFilter->getNewSystemGroup(); // // Create the tractor. // { // // Create the tractor body. // hkpRigidBody* tractorRigidBody; { hkpRigidBodyCinfo tractorInfo; tractorInfo.m_shape = tractorShape; tractorInfo.m_friction = 0.8f; tractorInfo.m_motionType = hkpMotion::MOTION_BOX_INERTIA; tractorInfo.m_collisionFilterInfo = hkpGroupFilter::calcFilterInfo(0, vehicleGroup ); // Position chassis on the ground. tractorInfo.m_position.set(0.0f, -3.0f, (vehicleId + 1) * 9.0f); //tractorInfo.m_angularDamping = 0.5f; // No need to set the inertia tensor, as this will be set automatically by the vehicle setup tractorInfo.m_mass = 15000.0f; tractorInfo.m_inertiaTensor.setDiagonal( 1.f, 1.f, 1.f ); tractorInfo.m_centerOfMass.set( -1.0f, -0.8f, 0.0f); tractorRigidBody = new hkpRigidBody(tractorInfo); } m_vehicles.expandOne(); TractorSetup tsetup; createTractorVehicle( tsetup, tractorRigidBody, m_vehicles.getSize()-1); tractorRigidBody->removeReference(); } // // Create the trailer body. // { hkpRigidBody* trailerRigidBody; { hkpRigidBodyCinfo trailerInfo; trailerInfo.m_shape = trailerShape; trailerInfo.m_friction = 0.8f; trailerInfo.m_motionType = hkpMotion::MOTION_BOX_INERTIA; trailerInfo.m_position.set(-9.0f, -3.0f, (1 + vehicleId) * 9.0f); trailerInfo.m_collisionFilterInfo = hkpGroupFilter::calcFilterInfo(0, vehicleGroup ); // No need to set the inertia tensor, as this will be set automatically by the vehicle setup trailerInfo.m_mass = 10000.0f; trailerInfo.m_inertiaTensor.setDiagonal( 1.f, 1.f, 1.f ); // Trailers are generally bottom-heavy to improve stability. // Move the centre of mass lower but keep it within the chassis limits. trailerInfo.m_centerOfMass.set(-1.0f, -1.0f, 0.0f); trailerRigidBody = new hkpRigidBody(trailerInfo); } m_vehicles.expandOne(); TrailerSetup tsetup; createTrailerVehicle( tsetup, trailerRigidBody, m_vehicles.getSize()-1); HK_SET_OBJECT_COLOR((hkUlong)trailerRigidBody->getCollidable(), 0x80ff8080); trailerRigidBody->removeReference(); } } tractorShape->removeReference(); trailerShape->removeReference(); } // // Create the wheels // createDisplayWheels(0.5f, 0.2f); // Attach Tractors and Trailers for (int vehicleId = 0; vehicleId < m_vehicles.getSize(); vehicleId+=2) { hkpRigidBody* tractor = m_vehicles[vehicleId].m_vehicle->getChassis(); hkpRigidBody* trailer = m_vehicles[vehicleId + 1 ].m_vehicle->getChassis(); // // Use a hinge constraint to connect the tractor to the trailer // if (0) { // // Set the pivot // hkVector4 axis(0.0f, 1.0f, 0.0f); hkVector4 point1(-3.5f, 0.4f, 0.0f); hkVector4 point2(6.0f, -0.4f, 0.0f); // Create constraint hkpHingeConstraintData* hc = new hkpHingeConstraintData(); hc->setInBodySpace(point1, point2, axis, axis); m_world->createAndAddConstraintInstance( tractor, trailer, hc )->removeReference(); hc->removeReference(); } // // Use a ball-and-socket constraint to connect the tractor to the trailer // else if (0) { hkVector4 point1(-3.5f, 0.4f, 0.0f); hkVector4 point2(6.0f, -0.4f, 0.0f); // Create the constraint hkpBallAndSocketConstraintData* bs = new hkpBallAndSocketConstraintData(); bs->setInBodySpace(point1, point2); m_world->createAndAddConstraintInstance( tractor, trailer, bs )->removeReference(); bs->removeReference(); } // // Use a ragdoll constraint to connect the tractor to the trailer // else if(1) { hkReal planeMin = HK_REAL_PI * -0.45f; hkReal planeMax = HK_REAL_PI * 0.45f; hkReal twistMin = HK_REAL_PI * -0.005f; hkReal twistMax = HK_REAL_PI * 0.005f; hkReal coneMin = HK_REAL_PI * -0.55f; hkReal coneMax = HK_REAL_PI * 0.55f; hkpRagdollConstraintData* rdc = new hkpRagdollConstraintData(); rdc->setPlaneMinAngularLimit(planeMin); rdc->setPlaneMaxAngularLimit(planeMax); rdc->setTwistMinAngularLimit(twistMin); rdc->setTwistMaxAngularLimit(twistMax); hkVector4 twistAxisA(1.0f, 0.0f, 0.0f); hkVector4 planeAxisA(0.0f, 0.0f, 1.0f); hkVector4 twistAxisB(1.0f, 0.0f, 0.0f); hkVector4 planeAxisB(0.0f, 0.0f, 1.0f); hkVector4 point1(-2.3f, -0.7f, 0.0f); hkVector4 point2( 7.2f, -1.3f, 0.0f); rdc->setInBodySpace(point1, point2, planeAxisA, planeAxisB, twistAxisA, twistAxisB); rdc->setAsymmetricConeAngle(coneMin, coneMax); m_world->createAndAddConstraintInstance(tractor, trailer, rdc)->removeReference(); rdc->removeReference(); } } m_world->unlock(); } // // Create the camera. // { hkp1dAngularFollowCamCinfo cinfo; cinfo.m_yawSignCorrection = 1.0f; cinfo.m_upDirWS.set(0.0f, 1.0f, 0.0f); cinfo.m_rigidBodyForwardDir.set(1.0f, 0.0f, 0.0f); cinfo.m_set[0].m_velocity = 10.0f; cinfo.m_set[1].m_velocity = 50.0f; cinfo.m_set[0].m_speedInfluenceOnCameraDirection = 1.0f; cinfo.m_set[1].m_speedInfluenceOnCameraDirection = 1.0f; cinfo.m_set[0].m_angularRelaxation = 3.5f; cinfo.m_set[1].m_angularRelaxation = 4.5f; // The two camera positions ("slow" and "fast" rest positions) are both the same here, // -6 units behind the chassis, and 2 units above it. Again, this is dependent on // m_chassisCoordinateSystem. cinfo.m_set[0].m_positionUS.set( -27.0f, 8.0f, 0.0f); cinfo.m_set[1].m_positionUS.set( -27.0f, 8.0f, 0.0f); cinfo.m_set[0].m_lookAtUS.set ( 2.0f, 0.0f, 0.0f ); cinfo.m_set[1].m_lookAtUS.set ( 2.0f, 0.0f, 0.0f ); cinfo.m_set[0].m_fov = 70.0f; cinfo.m_set[1].m_fov = 70.0f; m_camera.reinitialize( cinfo ); } }
// The scabbard is a single constraint which should have multiple degrees of freedom, but will suffer from the following // artifacts if implemented using a single ball-and-socket constraint: // 1. Undesired motion (excessive, potentially implausible, or just not artisitically desired) due to the simplification // to a single point of attachment. // 2. Excessive energy, or undersired enrgy preservation when the character comes to rest. // 3. A potential performance impact if collisions are used to prevent interpenetration with the character. // To address these we use a ragdoll constraint instead which allows full control to restict angular degrees of freedom of the // underlying ball-and-socket, and we use angular damping to make sure the scabbard comes to rest quickly. // We can then disable all collision between the scabbard and the hip, leaving all other collisions valid. void HK_CALL CharacterAttachmentsHelpers::addScabbard(hkpWorld* world, const hkaRagdollInstance* ragdollInstance, const hkQsTransform& currentTransform, const char* startBoneName, hkpGroupFilter* filter, const ConstraintStabilityTricks& tricks ) { // We will use a tricks to increase stability: const hkReal angularDamping = 4.0f; // Get the relevant RBs to which we attach this belt. hkpRigidBody* torso = HK_NULL; { int torsoIndex = hkaSkeletonUtils::findBoneWithName( *ragdollInstance->m_skeleton, startBoneName); HK_ASSERT2( 0, torsoIndex >= 0, "Couldn't find " << startBoneName << " in ragdoll" ); torso = ragdollInstance->getRigidBodyOfBone( torsoIndex ); } hkpRigidBody* scabbard; { hkVector4 boxHalfExtents( 0.02f, 0.05f, 0.3f); // Create body hkpRigidBodyCinfo info; info.m_shape = new hkpBoxShape( boxHalfExtents, 0.01f ); // We are going to add a single body which does not collide with the bone to which it is attached in the ragdoll so we // will put in the same group and disable collision using subgroups. hkUint32 torsoFilterInfo = torso->getCollidable()->getCollisionFilterInfo(); info.m_collisionFilterInfo = hkpGroupFilter::calcFilterInfo( 1, 2, 31, // New subgroup - larger than any subgroup in the ragdoll hkpGroupFilter::getSubSystemIdFromFilterInfo(torsoFilterInfo) // Don't collide with torso subgroup ); // Note that we use a scaled mass here to artificially damp the movement const hkReal mass = 1.0f; hkpInertiaTensorComputer::setShapeVolumeMassProperties( info.m_shape, mass, info ); info.m_mass = mass; // Note that we use an increased angular damping to further artificially damp the movement if( tricks.m_useDamping ) { info.m_angularDamping = angularDamping; } // Place roughly beside the character model space - we'll let a short simulation run settle the constraint properly. info.m_position.set( 0.2f, 0.0f, -0.2f ); // Move into world space behind character info.m_position.setTransformedPos(currentTransform, info.m_position); scabbard = new hkpRigidBody( info ); info.m_shape->removeReference(); world->addEntity( scabbard ); scabbard->removeReference(); } // // Create constraint (to attach and also restrict motion) // hkVector4 pivotA, pivotB; { // Scabbard pivotA.set(-0.02f, 0.0f, 0.3f); // Torso pivotB.set(0.0f, 0.0f, 0.2f); } hkpConstraintData* cd = HK_NULL; if( tricks.m_useLimits ) { hkReal planeMin = -HK_REAL_PI * 0.5f; hkReal planeMax = HK_REAL_PI * 0.05f; hkReal twistMin = HK_REAL_PI * -0.04f; hkReal twistMax = HK_REAL_PI * 0.04f; hkReal coneMin = HK_REAL_PI * 0.3f; hkpRagdollConstraintData* rdcd = new hkpRagdollConstraintData(); rdcd->setConeAngularLimit(coneMin); rdcd->setPlaneMinAngularLimit(planeMin); rdcd->setPlaneMaxAngularLimit(planeMax); rdcd->setTwistMinAngularLimit(twistMin); rdcd->setTwistMaxAngularLimit(twistMax); // Scabbard hkVector4 twistAxisA(0.0f, 0.0f, 1.0f); hkVector4 planeAxisA(1.0f, 0.0f, 0.0f); // Torso hkVector4 twistAxisB(1.0f, 0.0f, 0.0f); hkVector4 planeAxisB(0.0f, 0.0f, 1.0f); rdcd->setInBodySpace(pivotA, pivotB, planeAxisA, planeAxisB, twistAxisA, twistAxisB); cd = rdcd; } else { // Just use a ball-and-socket (no angular limits) hkpBallAndSocketConstraintData* bscd = new hkpBallAndSocketConstraintData(); bscd->setInBodySpace(pivotA, pivotB); cd = bscd; } // // Create and add the constraint // { hkpConstraintInstance* constraint = new hkpConstraintInstance(scabbard, torso, cd ); world->addConstraint(constraint); constraint->removeReference(); } cd->removeReference(); }