// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2021 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "SnippetVehicleCreate.h" #include "SnippetVehicleTireFriction.h" #include "SnippetVehicleSceneQuery.h" namespace snippetvehicle { using namespace physx; namespace fourwheel { void computeWheelCenterActorOffsets4W(const PxF32 wheelFrontZ, const PxF32 wheelRearZ, const PxVec3& chassisDims, const PxF32 wheelWidth, const PxF32 wheelRadius, const PxU32 numWheels, PxVec3* wheelCentreOffsets) { //chassisDims.z is the distance from the rear of the chassis to the front of the chassis. //The front has z = 0.5*chassisDims.z and the rear has z = -0.5*chassisDims.z. //Compute a position for the front wheel and the rear wheel along the z-axis. //Compute the separation between each wheel along the z-axis. const PxF32 numLeftWheels = numWheels/2.0f; const PxF32 deltaZ = (wheelFrontZ - wheelRearZ)/(numLeftWheels-1.0f); //Set the outside of the left and right wheels to be flush with the chassis. //Set the top of the wheel to be just touching the underside of the chassis. //Begin by setting the rear-left/rear-right/front-left,front-right wheels. wheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_LEFT] = PxVec3((-chassisDims.x + wheelWidth)*0.5f, -(chassisDims.y/2 + wheelRadius), wheelRearZ + 0*deltaZ*0.5f); wheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_RIGHT] = PxVec3((+chassisDims.x - wheelWidth)*0.5f, -(chassisDims.y/2 + wheelRadius), wheelRearZ + 0*deltaZ*0.5f); wheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_LEFT] = PxVec3((-chassisDims.x + wheelWidth)*0.5f, -(chassisDims.y/2 + wheelRadius), wheelRearZ + (numLeftWheels-1)*deltaZ); wheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT] = PxVec3((+chassisDims.x - wheelWidth)*0.5f, -(chassisDims.y/2 + wheelRadius), wheelRearZ + (numLeftWheels-1)*deltaZ); //Set the remaining wheels. for(PxU32 i = 2, wheelCount = 4; i < numWheels-2; i+=2, wheelCount+=2) { wheelCentreOffsets[wheelCount + 0] = PxVec3((-chassisDims.x + wheelWidth)*0.5f, -(chassisDims.y/2 + wheelRadius), wheelRearZ + i*deltaZ*0.5f); wheelCentreOffsets[wheelCount + 1] = PxVec3((+chassisDims.x - wheelWidth)*0.5f, -(chassisDims.y/2 + wheelRadius), wheelRearZ + i*deltaZ*0.5f); } } void setupWheelsSimulationData (const PxF32 wheelMass, const PxF32 wheelMOI, const PxF32 wheelRadius, const PxF32 wheelWidth, const PxU32 numWheels, const PxVec3* wheelCenterActorOffsets, const PxVec3& chassisCMOffset, const PxF32 chassisMass, PxVehicleWheelsSimData* wheelsSimData) { //Set up the wheels. PxVehicleWheelData wheels[PX_MAX_NB_WHEELS]; { //Set up the wheel data structures with mass, moi, radius, width. for(PxU32 i = 0; i < numWheels; i++) { wheels[i].mMass = wheelMass; wheels[i].mMOI = wheelMOI; wheels[i].mRadius = wheelRadius; wheels[i].mWidth = wheelWidth; } //Enable the handbrake for the rear wheels only. wheels[PxVehicleDrive4WWheelOrder::eREAR_LEFT].mMaxHandBrakeTorque=4000.0f; wheels[PxVehicleDrive4WWheelOrder::eREAR_RIGHT].mMaxHandBrakeTorque=4000.0f; //Enable steering for the front wheels only. wheels[PxVehicleDrive4WWheelOrder::eFRONT_LEFT].mMaxSteer=PxPi*0.3333f; wheels[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT].mMaxSteer=PxPi*0.3333f; } //Set up the tires. PxVehicleTireData tires[PX_MAX_NB_WHEELS]; { //Set up the tires. for(PxU32 i = 0; i < numWheels; i++) { tires[i].mType = TIRE_TYPE_NORMAL; } } //Set up the suspensions PxVehicleSuspensionData suspensions[PX_MAX_NB_WHEELS]; { //Compute the mass supported by each suspension spring. PxF32 suspSprungMasses[PX_MAX_NB_WHEELS]; PxVehicleComputeSprungMasses (numWheels, wheelCenterActorOffsets, chassisCMOffset, chassisMass, 1, suspSprungMasses); //Set the suspension data. for(PxU32 i = 0; i < numWheels; i++) { suspensions[i].mMaxCompression = 0.3f; suspensions[i].mMaxDroop = 0.1f; suspensions[i].mSpringStrength = 35000.0f; suspensions[i].mSpringDamperRate = 4500.0f; suspensions[i].mSprungMass = suspSprungMasses[i]; } //Set the camber angles. const PxF32 camberAngleAtRest=0.0; const PxF32 camberAngleAtMaxDroop=0.01f; const PxF32 camberAngleAtMaxCompression=-0.01f; for(PxU32 i = 0; i < numWheels; i+=2) { suspensions[i + 0].mCamberAtRest = camberAngleAtRest; suspensions[i + 1].mCamberAtRest = -camberAngleAtRest; suspensions[i + 0].mCamberAtMaxDroop = camberAngleAtMaxDroop; suspensions[i + 1].mCamberAtMaxDroop = -camberAngleAtMaxDroop; suspensions[i + 0].mCamberAtMaxCompression = camberAngleAtMaxCompression; suspensions[i + 1].mCamberAtMaxCompression = -camberAngleAtMaxCompression; } } //Set up the wheel geometry. PxVec3 suspTravelDirections[PX_MAX_NB_WHEELS]; PxVec3 wheelCentreCMOffsets[PX_MAX_NB_WHEELS]; PxVec3 suspForceAppCMOffsets[PX_MAX_NB_WHEELS]; PxVec3 tireForceAppCMOffsets[PX_MAX_NB_WHEELS]; { //Set the geometry data. for(PxU32 i = 0; i < numWheels; i++) { //Vertical suspension travel. suspTravelDirections[i] = PxVec3(0,-1,0); //Wheel center offset is offset from rigid body center of mass. wheelCentreCMOffsets[i] = wheelCenterActorOffsets[i] - chassisCMOffset; //Suspension force application point 0.3 metres below //rigid body center of mass. suspForceAppCMOffsets[i] = PxVec3(wheelCentreCMOffsets[i].x,-0.3f,wheelCentreCMOffsets[i].z); //Tire force application point 0.3 metres below //rigid body center of mass. tireForceAppCMOffsets[i] = PxVec3(wheelCentreCMOffsets[i].x,-0.3f,wheelCentreCMOffsets[i].z); } } //Set up the filter data of the raycast that will be issued by each suspension. PxFilterData qryFilterData; setupNonDrivableSurface(qryFilterData); //Set the wheel, tire and suspension data. //Set the geometry data. //Set the query filter data for(PxU32 i = 0; i < numWheels; i++) { wheelsSimData->setWheelData(i, wheels[i]); wheelsSimData->setTireData(i, tires[i]); wheelsSimData->setSuspensionData(i, suspensions[i]); wheelsSimData->setSuspTravelDirection(i, suspTravelDirections[i]); wheelsSimData->setWheelCentreOffset(i, wheelCentreCMOffsets[i]); wheelsSimData->setSuspForceAppPointOffset(i, suspForceAppCMOffsets[i]); wheelsSimData->setTireForceAppPointOffset(i, tireForceAppCMOffsets[i]); wheelsSimData->setSceneQueryFilterData(i, qryFilterData); wheelsSimData->setWheelShapeMapping(i, PxI32(i)); } //Add a front and rear anti-roll bar PxVehicleAntiRollBarData barFront; barFront.mWheel0 = PxVehicleDrive4WWheelOrder::eFRONT_LEFT; barFront.mWheel1 = PxVehicleDrive4WWheelOrder::eFRONT_RIGHT; barFront.mStiffness = 10000.0f; wheelsSimData->addAntiRollBarData(barFront); PxVehicleAntiRollBarData barRear; barRear.mWheel0 = PxVehicleDrive4WWheelOrder::eREAR_LEFT; barRear.mWheel1 = PxVehicleDrive4WWheelOrder::eREAR_RIGHT; barRear.mStiffness = 10000.0f; wheelsSimData->addAntiRollBarData(barRear); } } //namespace fourwheel PxVehicleDrive4W* createVehicle4W(const VehicleDesc& vehicle4WDesc, PxPhysics* physics, PxCooking* cooking) { const PxVec3 chassisDims = vehicle4WDesc.chassisDims; const PxF32 wheelWidth = vehicle4WDesc.wheelWidth; const PxF32 wheelRadius = vehicle4WDesc.wheelRadius; const PxU32 numWheels = vehicle4WDesc.numWheels; const PxFilterData& chassisSimFilterData = vehicle4WDesc.chassisSimFilterData; const PxFilterData& wheelSimFilterData = vehicle4WDesc.wheelSimFilterData; //Construct a physx actor with shapes for the chassis and wheels. //Set the rigid body mass, moment of inertia, and center of mass offset. PxRigidDynamic* veh4WActor = NULL; { //Construct a convex mesh for a cylindrical wheel. PxConvexMesh* wheelMesh = createWheelMesh(wheelWidth, wheelRadius, *physics, *cooking); //Assume all wheels are identical for simplicity. PxConvexMesh* wheelConvexMeshes[PX_MAX_NB_WHEELS]; PxMaterial* wheelMaterials[PX_MAX_NB_WHEELS]; //Set the meshes and materials for the driven wheels. for(PxU32 i = PxVehicleDrive4WWheelOrder::eFRONT_LEFT; i <= PxVehicleDrive4WWheelOrder::eREAR_RIGHT; i++) { wheelConvexMeshes[i] = wheelMesh; wheelMaterials[i] = vehicle4WDesc.wheelMaterial; } //Set the meshes and materials for the non-driven wheels for(PxU32 i = PxVehicleDrive4WWheelOrder::eREAR_RIGHT + 1; i < numWheels; i++) { wheelConvexMeshes[i] = wheelMesh; wheelMaterials[i] = vehicle4WDesc.wheelMaterial; } //Chassis just has a single convex shape for simplicity. PxConvexMesh* chassisConvexMesh = createChassisMesh(chassisDims, *physics, *cooking); PxConvexMesh* chassisConvexMeshes[1] = {chassisConvexMesh}; PxMaterial* chassisMaterials[1] = {vehicle4WDesc.chassisMaterial}; //Rigid body data. PxVehicleChassisData rigidBodyData; rigidBodyData.mMOI = vehicle4WDesc.chassisMOI; rigidBodyData.mMass = vehicle4WDesc.chassisMass; rigidBodyData.mCMOffset = vehicle4WDesc.chassisCMOffset; veh4WActor = createVehicleActor (rigidBodyData, wheelMaterials, wheelConvexMeshes, numWheels, wheelSimFilterData, chassisMaterials, chassisConvexMeshes, 1, chassisSimFilterData, *physics); } //Set up the sim data for the wheels. PxVehicleWheelsSimData* wheelsSimData = PxVehicleWheelsSimData::allocate(numWheels); { //Compute the wheel center offsets from the origin. PxVec3 wheelCenterActorOffsets[PX_MAX_NB_WHEELS]; const PxF32 frontZ = chassisDims.z*0.3f; const PxF32 rearZ = -chassisDims.z*0.3f; fourwheel::computeWheelCenterActorOffsets4W(frontZ, rearZ, chassisDims, wheelWidth, wheelRadius, numWheels, wheelCenterActorOffsets); //Set up the simulation data for all wheels. fourwheel::setupWheelsSimulationData (vehicle4WDesc.wheelMass, vehicle4WDesc.wheelMOI, wheelRadius, wheelWidth, numWheels, wheelCenterActorOffsets, vehicle4WDesc.chassisCMOffset, vehicle4WDesc.chassisMass, wheelsSimData); } //Set up the sim data for the vehicle drive model. PxVehicleDriveSimData4W driveSimData; { //Diff PxVehicleDifferential4WData diff; diff.mType=PxVehicleDifferential4WData::eDIFF_TYPE_LS_4WD; driveSimData.setDiffData(diff); //Engine PxVehicleEngineData engine; engine.mPeakTorque=500.0f; engine.mMaxOmega=600.0f;//approx 6000 rpm driveSimData.setEngineData(engine); //Gears PxVehicleGearsData gears; gears.mSwitchTime=0.5f; driveSimData.setGearsData(gears); //Clutch PxVehicleClutchData clutch; clutch.mStrength=10.0f; driveSimData.setClutchData(clutch); //Ackermann steer accuracy PxVehicleAckermannGeometryData ackermann; ackermann.mAccuracy=1.0f; ackermann.mAxleSeparation= wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).z- wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).z; ackermann.mFrontWidth= wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT).x- wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT).x; ackermann.mRearWidth= wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT).x - wheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT).x; driveSimData.setAckermannGeometryData(ackermann); } //Create a vehicle from the wheels and drive sim data. PxVehicleDrive4W* vehDrive4W = PxVehicleDrive4W::allocate(numWheels); vehDrive4W->setup(physics, veh4WActor, *wheelsSimData, driveSimData, numWheels - 4); //Configure the userdata configureUserData(vehDrive4W, vehicle4WDesc.actorUserData, vehicle4WDesc.shapeUserDatas); //Free the sim data because we don't need that any more. wheelsSimData->free(); return vehDrive4W; } } //namespace snippetvehicle