#ifndef FRICTION_MAP_VEHICLE_RAYCAST_COLLIDE_H #define FRICTION_MAP_VEHICLE_RAYCAST_COLLIDE_H #include #include #include extern const class hkClass FrictionMapVehicleRaycastWheelCollideClass; #define VEHICLE_RAYCAST_ICY_FRICTION_PROPERTY 67341 // This is the class which implements the calcSingleWheelGroundFriction, allowing you to override the // ground friction calculation for raycast vehicles. Here, as a very simple example, we overwrite the friction to be // 0.01 for all bodies (upon which we are driving) which have been tagged with a VEHICLE_RAYCAST_ICY_FRICTION_PROPERTY // user property, otherwise we leave the friction as it is. // In general of course, the friction value need not be constant over the body (or time), and any function can be used // here, for example a lookup into your own full 2D 'friction map'. // Other ways to store/retrieve friction values would be via: // hkpShape user data // A 'float' hkpProperty stored with a the rigid body // hkMeshMaterials for hkMeshShapes // class FrictionMapVehicleRaycastWheelCollide : public hkpVehicleRayCastWheelCollide { public: HK_DECLARE_CLASS_ALLOCATOR(HK_MEMORY_CLASS_BASE); private: virtual void wheelCollideCallback( const hkpVehicleInstance* vehicle, hkUint8 wheelIndex, CollisionDetectionWheelOutput& cdInfo ) { hkpRigidBody* rb = cdInfo.m_contactBody; if ( rb ) { if ( rb->hasProperty(VEHICLE_RAYCAST_ICY_FRICTION_PROPERTY) ) { cdInfo.m_contactFriction = 0.01f; } } } // Serialization. public: // By adding HK_DECLARE_REFLECTION, we enable objects of this class to be serialized. // However, the class does need to be registered with the type registry. The following code can be used: // hkBuiltinTypeRegistry::getInstance().addType( &FrictionMapVehicleRaycastWheelCollideTypeInfo, &FrictionMapVehicleRaycastWheelCollideClass ); HK_DECLARE_REFLECTION(); FrictionMapVehicleRaycastWheelCollide () { } FrictionMapVehicleRaycastWheelCollide ( hkFinishLoadedObjectFlag f ) : hkpVehicleRayCastWheelCollide( f ) { } }; #endif // FRICTION_MAP_VEHICLE_RAYCAST_COLLIDE_H #ifndef HK_VEHICLE_SETUP_H #define HK_VEHICLE_SETUP_H #include #include #include #include #include #include #include #include #include #include #include /// This class just holds ALL the vehicle parameters (hardcoded) for an untuned, four /// wheeled vehicle and facilitates construction via blueprints on a single call to /// buildVehicle(). class VehicleSetup : public hkReferencedObject { public: HK_DECLARE_CLASS_ALLOCATOR( HK_MEMORY_CLASS_VEHICLE ); VehicleSetup(){} virtual void buildVehicle( const hkpWorld* world, hkpVehicleInstance& vehicle ); public: virtual void setupVehicleData( const hkpWorld* world, hkpVehicleData& data ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultAnalogDriverInput& driverInput ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultSteering& steering ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultEngine& engine ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultTransmission& transmission ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultBrake& brake ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultSuspension& suspension ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultAerodynamics& aerodynamics ); virtual void setupComponent( const hkpVehicleData& data, hkpVehicleDefaultVelocityDamper& velocityDamper ); virtual void setupWheelCollide( const hkpWorld* world, const hkpVehicleInstance& vehicle, hkpVehicleRayCastWheelCollide& wheelCollide ); virtual void setupTyremarks( const hkpVehicleData& data, hkpTyremarksInfo& tyremarkscontroller ); }; #endif // HK_VEHICLE_SETUP_H void VehicleSetup::buildVehicle( const hkpWorld* world, hkpVehicleInstance& vehicle ) { // // All memory allocations are made here. // vehicle.m_data = new hkpVehicleData; vehicle.m_driverInput = new hkpVehicleDefaultAnalogDriverInput; vehicle.m_steering = new hkpVehicleDefaultSteering; vehicle.m_engine = new hkpVehicleDefaultEngine; vehicle.m_transmission = new hkpVehicleDefaultTransmission; vehicle.m_brake = new hkpVehicleDefaultBrake; vehicle.m_suspension = new hkpVehicleDefaultSuspension; vehicle.m_aerodynamics = new hkpVehicleDefaultAerodynamics; vehicle.m_velocityDamper = new hkpVehicleDefaultVelocityDamper; // For illustrative purposes we use a custom hkpVehicleRayCastWheelCollide // which implements varying 'ground' friction in a very simple way. vehicle.m_wheelCollide = new FrictionMapVehicleRaycastWheelCollide; setupVehicleData( world, *vehicle.m_data ); // initialise the tyremarks controller with 128 tyremark points. vehicle.m_tyreMarks = new hkpTyremarksInfo( *vehicle.m_data, 128 ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultAnalogDriverInput* >(vehicle.m_driverInput) ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultSteering*>(vehicle.m_steering)); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultEngine*>(vehicle.m_engine) ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultTransmission*>(vehicle.m_transmission) ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultBrake*>(vehicle.m_brake) ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultSuspension*>(vehicle.m_suspension) ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultAerodynamics*>(vehicle.m_aerodynamics) ); setupComponent( *vehicle.m_data, *static_cast< hkpVehicleDefaultVelocityDamper*>(vehicle.m_velocityDamper) ); setupWheelCollide( world, vehicle, *static_cast< hkpVehicleRayCastWheelCollide*>(vehicle.m_wheelCollide) ); setupTyremarks( *vehicle.m_data, *static_cast< hkpTyremarksInfo*>(vehicle.m_tyreMarks) ); // // Check that all components are present. // HK_ASSERT(0x0, vehicle.m_data ); HK_ASSERT(0x7708674a, vehicle.m_driverInput ); HK_ASSERT(0x5a324a2d, vehicle.m_steering ); HK_ASSERT(0x7bcb2aff, vehicle.m_engine ); HK_ASSERT(0x29bddb50, vehicle.m_transmission ); HK_ASSERT(0x2b0323a2, vehicle.m_brake ); HK_ASSERT(0x7a7ade23, vehicle.m_suspension ); HK_ASSERT(0x6ec4d0ed, vehicle.m_aerodynamics ); HK_ASSERT(0x67161206, vehicle.m_wheelCollide ); HK_ASSERT(0x295015f1, vehicle.m_tyreMarks ); // // Set up any variables that store cached data. // // Give driver input default values so that the vehicle (if this input is a default for non // player cars) will drive, even if it is in circles! // Accelerate. vehicle.m_deviceStatus = new hkpVehicleDriverInputAnalogStatus; hkpVehicleDriverInputAnalogStatus* deviceStatus = (hkpVehicleDriverInputAnalogStatus*)vehicle.m_deviceStatus; deviceStatus->m_positionY = -0.4f; // Turn. deviceStatus->m_positionX = 0.3f; // Defaults deviceStatus->m_handbrakeButtonPressed = false; deviceStatus->m_reverseButtonPressed = false; // // Don't forget to call init! (This function is necessary to set up derived data) // vehicle.init(); } void VehicleSetup::setupVehicleData( const hkpWorld* world, hkpVehicleData& data ) { data.m_gravity = world->getGravity(); // // The vehicleData contains information about the chassis. // // The coordinates of the chassis system, used for steering the vehicle. // up forward right data.m_chassisOrientation.setCols( hkVector4(0, 1, 0), hkVector4(1, 0, 0), hkVector4(0, 0, 1)); data.m_frictionEqualizer = 0.5f; // Inertia tensor for each axis is calculated by using : // (1 / chassis_mass) * (torque(axis)Factor / chassisUnitInertia) data.m_torqueRollFactor = 0.625f; data.m_torquePitchFactor = 0.5f; data.m_torqueYawFactor = 0.35f; data.m_chassisUnitInertiaYaw = 1.0f; data.m_chassisUnitInertiaRoll = 1.0f; data.m_chassisUnitInertiaPitch = 1.0f; // Adds or removes torque around the yaw axis // based on the current steering angle. This will // affect steering. data.m_extraTorqueFactor = -0.5f; data.m_maxVelocityForPositionalFriction = 0.0f; // // Wheel specifications // data.m_numWheels = 4; data.m_wheelParams.setSize( data.m_numWheels ); data.m_wheelParams[0].m_axle = 0; data.m_wheelParams[1].m_axle = 0; data.m_wheelParams[2].m_axle = 1; data.m_wheelParams[3].m_axle = 1; data.m_wheelParams[0].m_friction = 1.5f; data.m_wheelParams[1].m_friction = 1.5f; data.m_wheelParams[2].m_friction = 1.5f; data.m_wheelParams[3].m_friction = 1.5f; data.m_wheelParams[0].m_slipAngle = 0.0f; data.m_wheelParams[1].m_slipAngle = 0.0f; data.m_wheelParams[2].m_slipAngle = 0.0f; data.m_wheelParams[3].m_slipAngle = 0.0f; for ( int i = 0 ; i < data.m_numWheels ; i++ ) { // This value is also used to calculate the m_primaryTransmissionRatio. data.m_wheelParams[i].m_radius = 0.4f; data.m_wheelParams[i].m_width = 0.2f; data.m_wheelParams[i].m_mass = 10.0f; data.m_wheelParams[i].m_viscosityFriction = 0.25f; data.m_wheelParams[i].m_maxFriction = 2.0f * data.m_wheelParams[i].m_friction; data.m_wheelParams[i].m_forceFeedbackMultiplier = 0.1f; data.m_wheelParams[i].m_maxContactBodyAcceleration = hkReal(data.m_gravity.length3()) * 2; } } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultAnalogDriverInput& driverInput ) { // We also use an analog "driver input" class to help converting user input to vehicle behavior. driverInput.m_slopeChangePointX = 0.8f; driverInput.m_initialSlope = 0.7f; driverInput.m_deadZone = 0.0f; driverInput.m_autoReverse = true; } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultSteering& steering ) { steering.m_doesWheelSteer.setSize( data.m_numWheels ); // degrees steering.m_maxSteeringAngle = 35 * ( HK_REAL_PI / 180 ); // [mph/h] The steering angle decreases linearly // based on your overall max speed of the vehicle. steering.m_maxSpeedFullSteeringAngle = 70.0f * (1.605f / 3.6f); steering.m_doesWheelSteer[0] = true; steering.m_doesWheelSteer[1] = true; steering.m_doesWheelSteer[2] = false; steering.m_doesWheelSteer[3] = false; } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultEngine& engine ) { engine.m_maxTorque = 500.0f; engine.m_minRPM = 1000.0f; engine.m_optRPM = 5500.0f; // This value is also used to calculate the m_primaryTransmissionRatio. engine.m_maxRPM = 7500.0f; engine.m_torqueFactorAtMinRPM = 0.8f; engine.m_torqueFactorAtMaxRPM = 0.8f; engine.m_resistanceFactorAtMinRPM = 0.05f; engine.m_resistanceFactorAtOptRPM = 0.1f; engine.m_resistanceFactorAtMaxRPM = 0.3f; } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultTransmission& transmission ) { int numGears = 4; transmission.m_gearsRatio.setSize( numGears ); transmission.m_wheelsTorqueRatio.setSize( data.m_numWheels ); transmission.m_downshiftRPM = 3500.0f; transmission.m_upshiftRPM = 6500.0f; transmission.m_clutchDelayTime = 0.0f; transmission.m_reverseGearRatio = 1.0f; transmission.m_gearsRatio[0] = 2.0f; transmission.m_gearsRatio[1] = 1.5f; transmission.m_gearsRatio[2] = 1.0f; transmission.m_gearsRatio[3] = 0.75f; transmission.m_wheelsTorqueRatio[0] = 0.2f; transmission.m_wheelsTorqueRatio[1] = 0.2f; transmission.m_wheelsTorqueRatio[2] = 0.3f; transmission.m_wheelsTorqueRatio[3] = 0.3f; const hkReal vehicleTopSpeed = 130.0f; const hkReal wheelRadius = 0.4f; const hkReal maxEngineRpm = 7500.0f; transmission.m_primaryTransmissionRatio = hkpVehicleDefaultTransmission::calculatePrimaryTransmissionRatio( vehicleTopSpeed, wheelRadius, maxEngineRpm, transmission.m_gearsRatio[ numGears - 1 ] ); } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultBrake& brake ) { brake.m_wheelBrakingProperties.setSize( data.m_numWheels ); const float bt = 1500.0f; brake.m_wheelBrakingProperties[0].m_maxBreakingTorque = bt; brake.m_wheelBrakingProperties[1].m_maxBreakingTorque = bt; brake.m_wheelBrakingProperties[2].m_maxBreakingTorque = bt; brake.m_wheelBrakingProperties[3].m_maxBreakingTorque = bt; // Handbrake is attached to rear wheels only. brake.m_wheelBrakingProperties[0].m_isConnectedToHandbrake = false; brake.m_wheelBrakingProperties[1].m_isConnectedToHandbrake = false; brake.m_wheelBrakingProperties[2].m_isConnectedToHandbrake = true; brake.m_wheelBrakingProperties[3].m_isConnectedToHandbrake = true; brake.m_wheelBrakingProperties[0].m_minPedalInputToBlock = 0.9f; brake.m_wheelBrakingProperties[1].m_minPedalInputToBlock = 0.9f; brake.m_wheelBrakingProperties[2].m_minPedalInputToBlock = 0.9f; brake.m_wheelBrakingProperties[3].m_minPedalInputToBlock = 0.9f; brake.m_wheelsMinTimeToBlock = 1000.0f; } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultSuspension& suspension ) { suspension.m_wheelParams.setSize( data.m_numWheels ); suspension.m_wheelSpringParams.setSize( data.m_numWheels ); suspension.m_wheelParams[0].m_length = 0.35f; suspension.m_wheelParams[1].m_length = 0.35f; suspension.m_wheelParams[2].m_length = 0.35f; suspension.m_wheelParams[3].m_length = 0.35f; const float str = 50.0f; suspension.m_wheelSpringParams[0].m_strength = str; suspension.m_wheelSpringParams[1].m_strength = str; suspension.m_wheelSpringParams[2].m_strength = str; suspension.m_wheelSpringParams[3].m_strength = str; const float wd = 3.0f; suspension.m_wheelSpringParams[0].m_dampingCompression = wd; suspension.m_wheelSpringParams[1].m_dampingCompression = wd; suspension.m_wheelSpringParams[2].m_dampingCompression = wd; suspension.m_wheelSpringParams[3].m_dampingCompression = wd; suspension.m_wheelSpringParams[0].m_dampingRelaxation = wd; suspension.m_wheelSpringParams[1].m_dampingRelaxation = wd; suspension.m_wheelSpringParams[2].m_dampingRelaxation = wd; suspension.m_wheelSpringParams[3].m_dampingRelaxation = wd; // // NB: The hardpoints MUST be positioned INSIDE the chassis. // { const hkReal hardPointFrontX = 1.3f; const hkReal hardPointBackX = -1.1f; const hkReal hardPointY = -0.05f; const hkReal hardPointZ = 1.1f; suspension.m_wheelParams[0].m_hardpointChassisSpace.set ( hardPointFrontX, hardPointY, -hardPointZ); suspension.m_wheelParams[1].m_hardpointChassisSpace.set ( hardPointFrontX, hardPointY, hardPointZ); suspension.m_wheelParams[2].m_hardpointChassisSpace.set ( hardPointBackX, hardPointY, -hardPointZ); suspension.m_wheelParams[3].m_hardpointChassisSpace.set ( hardPointBackX, hardPointY, hardPointZ); } const hkVector4 downDirection( 0.0f, -1.0f, 0.0f ); suspension.m_wheelParams[0].m_directionChassisSpace = downDirection; suspension.m_wheelParams[1].m_directionChassisSpace = downDirection; suspension.m_wheelParams[2].m_directionChassisSpace = downDirection; suspension.m_wheelParams[3].m_directionChassisSpace = downDirection; } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultAerodynamics& aerodynamics ) { aerodynamics.m_airDensity = 1.3f; // In m^2. aerodynamics.m_frontalArea = 1.0f; aerodynamics.m_dragCoefficient = 0.7f; aerodynamics.m_liftCoefficient = -0.3f; // Extra gavity applies in world space (independent of m_chassisCoordinateSystem). aerodynamics.m_extraGravityws.set( 0.0f, -5.0f, 0.0f); } void VehicleSetup::setupComponent( const hkpVehicleData& data, hkpVehicleDefaultVelocityDamper& velocityDamper ) { // Caution: setting negative damping values will add energy to system. // Setting the value to 0 will not affect the angular velocity. // Damping the change of the chassis angular velocity when below m_collisionThreshold. // This will affect turning radius and steering. velocityDamper.m_normalSpinDamping = 0.0f; // Positive numbers dampen the rotation of the chassis and // reduce the reaction of the chassis in a collision. velocityDamper.m_collisionSpinDamping = 4.0f; // The threshold in m/s at which the algorithm switches from // using the normalSpinDamping to the collisionSpinDamping. velocityDamper.m_collisionThreshold = 1.0f; } void VehicleSetup::setupWheelCollide( const hkpWorld* world, const hkpVehicleInstance& vehicle, hkpVehicleRayCastWheelCollide& wheelCollide ) { // Set the wheels to have the same collision filter info as the chassis. wheelCollide.m_wheelCollisionFilterInfo = vehicle.getChassis()->getCollisionFilterInfo(); } void VehicleSetup::setupTyremarks( const hkpVehicleData& data, hkpTyremarksInfo& tyreMarks ) { tyreMarks.m_minTyremarkEnergy = 100.0f; tyreMarks.m_maxTyremarkEnergy = 1000.0f; } hkpConvexVerticesShape* createCarChassisShape() { hkReal xSize = 1.75f; hkReal ySize = 0.25f; hkReal zSize = 1.1f; hkReal xBumper = 1.9f; hkReal yBumper = 0.15f; hkReal zBumper = 1.0f; hkReal xRoofFront = 0.4f; hkReal xRoofBack = -1.0f; hkReal yRoof = ySize + 0.45f; hkReal zRoof = 0.7f; hkReal xDoorFront = xRoofFront; hkReal xDoorBack = xRoofBack; hkReal yDoor = ySize; hkReal zDoor = zSize + 0.1f; int numVertices = 22; // 16 = 4 (size of "each float group", 3 for x,y,z, 1 for padding) * 4 (size of float). int stride = sizeof(hkReal) * 4; HK_ALIGN16(hkReal vertices[] )= { xSize, ySize, zSize, 0.0f, // v0 xSize, 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 xBumper, yBumper, zBumper, 0.0f, // v6 xBumper, yBumper, -zBumper, 0.0f, // v7 -xBumper, yBumper, zBumper, 0.0f, // v8 -xBumper, yBumper, -zBumper, 0.0f, // v9 xRoofFront, yRoof, zRoof, 0.0f, // v10 xRoofFront, yRoof, -zRoof, 0.0f, // v11 xRoofBack, yRoof, zRoof, 0.0f, // v12 xRoofBack, yRoof, -zRoof, 0.0f, // v13 xDoorFront, yDoor, zDoor, 0.0f, // v14 xDoorFront, yDoor, -zDoor, 0.0f, // v15 xDoorFront, -yDoor, zDoor, 0.0f, // v16 xDoorFront, -yDoor, -zDoor, 0.0f, // v17 xDoorBack, yDoor, zDoor, 0.0f, // v18 xDoorBack, yDoor, -zDoor, 0.0f, // v19 xDoorBack, -yDoor, zDoor, 0.0f, // v20 xDoorBack, -yDoor, -zDoor, 0.0f, // v21 }; // // SHAPE CONSTRUCTION. // hkStridedVertices stridedVerts; stridedVerts.m_numVertices = numVertices; stridedVerts.m_striding = stride; stridedVerts.m_vertices = vertices; return new hkpConvexVerticesShape(stridedVerts); } hkpVehicleInstance* m_vehicle; hkpConvexVerticesShape* chassisShape = createCarChassisShape(); // hkpSphereShape* chassisShape = new hkpSphereShape(0.15f); // hkpRigidBody* chassis; hkpRigidBody* chassisRigidBody; void Pojazd() { { hkpRigidBodyCinfo chassisInfo; chassisInfo.m_mass = 750.0f; chassisInfo.m_shape = chassisShape; chassisInfo.m_friction = 0.4f; chassisInfo.m_motionType = hkpMotion::MOTION_BOX_INERTIA; chassisInfo.m_position.set(0.0f, 1.0f, 0.0f); hkpInertiaTensorComputer::setShapeVolumeMassProperties(chassisInfo.m_shape, chassisInfo.m_mass, chassisInfo); chassisRigidBody = new hkpRigidBody(chassisInfo); // No longer need reference to shape as the hkpRigidBody holds one. chassisShape->removeReference(); m_world->addEntity(chassisRigidBody); } // createVehicle( chassisRigidBody ); m_vehicle = new hkpVehicleInstance( chassisRigidBody ); VehicleSetup setup; setup.buildVehicle( m_world, *m_vehicle ); m_vehicle->addToWorld( m_world ); m_world->addAction(m_vehicle); chassisRigidBody->removeReference(); }