// // 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) 2018 NVIDIA Corporation. All rights reserved. #include "SceneVehicle.h" #include "Convex.h" #include "PxSimpleFactory.h" #include "PxRigidStatic.h" #include "PxShape.h" #include "foundation/PxMathUtils.h" #include "foundation/PxMat44.h" #include "PsString.h" #include "CmPhysxCommon.h" #include #include #include "SimScene.h" #include "CompoundCreator.h" #include "TerrainMesh.h" #include "PhysXMacros.h" #include "PxRigidBodyExt.h" #include "vehicle/PxVehicleSDK.h" #include "vehicle/PxVehicleDrive4W.h" #include "vehicle/PxVehicleUtilSetup.h" #include "vehicle/PxVehicleUtil.h" #include "SceneVehicleCooking.h" #include "SceneVehicleSceneQuery.h" #include "SampleViewerGamepad.h" #include "RawLoader.h" #include "TerrainRandomSamplePrecompute.h" #include "MediaPath.h" #include "glmesh.h" #include "Texture.h" #include "PxShapeExt.h" static bool createVehicleDemo = true; /////////////////////////////////// //VEHICLE START PARAMETERS /////////////////////////////////// #define NUM_PLAYER_CARS 1 static PxTransform gPlayerCarStartTransforms[NUM_PLAYER_CARS] = { PxTransform(PxVec3(-154.699753f, 9.863837f, 87.684113f), PxQuat(-0.000555f, -0.267015, 0.000155, -0.963692)) }; //////////////////////////////////////////////////////////////// //VEHICLE SETUP DATA //////////////////////////////////////////////////////////////// PxF32 gChassisMass = 1500.0f; PxF32 gSuspensionShimHeight = 0.125f; //////////////////////////////////////////////////////////////// //RENDER USER DATA TO ASSOCIATE EACH RENDER MESH WITH A //VEHICLE PHYSICS COMPONENT //////////////////////////////////////////////////////////////// enum { CAR_PART_FRONT_LEFT_WHEEL = 0, CAR_PART_FRONT_RIGHT_WHEEL, CAR_PART_REAR_LEFT_WHEEL, CAR_PART_REAR_RIGHT_WHEEL, CAR_PART_CHASSIS, CAR_PART_WINDOWS, NUM_CAR4W_RENDER_COMPONENTS, CAR_PART_EXTRA_WHEEL0 = NUM_CAR4W_RENDER_COMPONENTS, CAR_PART_EXTRA_WHEEL1, NUM_CAR6W_RENDER_COMPONENTS }; enum MaterialID { MATERIAL_TERRAIN_MUD = 1000, MATERIAL_ROAD_TARMAC = 1001, MATERIAL_ROAD_SNOW = 1002, MATERIAL_ROAD_GRASS = 1003, }; static const char gCarPartNames[NUM_CAR4W_RENDER_COMPONENTS][64] = { "frontwheelleftshape", "frontwheelrightshape", "backwheelleftshape", "backwheelrightshape", "car_02_visshape", "car_02_windowsshape" }; //////////////////////////////////////////////////////////////// //TRANSFORM APPLIED TO CHASSIS RENDER MESH VERTS //THAT IS REQUIRED TO PLACE AABB OF CHASSIS RENDER MESH AT ORIGIN //AT CENTRE-POINT OF WHEELS. //////////////////////////////////////////////////////////////// static PxVec3 gChassisMeshTransform(0, 0, 0); //////////////////////////////////////////////////////////////// //WHEEL CENTRE OFFSETS FROM CENTRE OF CHASSIS RENDER MESH AABB //OF 4-WHEELED VEHICLE //////////////////////////////////////////////////////////////// static PxVec3 gWheelCentreOffsets4[4]; //////////////////////////////////////////////////////////////// //CONVEX HULL OF RENDER MESH FOR CHASSIS AND WHEELS OF //4-WHEELED VEHICLE //////////////////////////////////////////////////////////////// static PxConvexMesh* gChassisConvexMesh = NULL; static PxConvexMesh* gWheelConvexMeshes4[4] = { NULL, NULL, NULL, NULL }; static GLMesh* gCarPartRenderMesh[6] = { NULL, NULL, NULL, NULL, NULL, NULL }; // ---------------------------------------------------------------------------------------------- SceneVehicle::SceneVehicle(PxPhysics* pxPhysics, PxCooking *pxCooking, bool isGrb, Shader *defaultShader, const char *resourcePath, float slowMotionFactor) : SceneKapla(pxPhysics, pxCooking, isGrb, defaultShader, resourcePath, slowMotionFactor) { mChassisMaterialDrivable = NULL; mChassisMaterialNonDrivable = NULL; if (createVehicleDemo) mCameraDisable = true; else mCameraDisable = false; mNumTextures = 0; mNumMaterials = 0; //onInit(pxPhysics, pxCooking, pxScene); } void SceneVehicle::onInit(PxScene* pxScene) { SceneKapla::onInit(pxScene); createStandardMaterials(); if (createVehicleDemo) createVehicle(mPxPhysics); SceneKapla::createTerrain("terrain_ll2.bmp", ""); const PxVec3 dims(0.08f, 0.25f, 1.0f); PxMaterial* DefaultMaterial = mPxPhysics->createMaterial(0.5f, 0.25f, 0.1f); ShaderMaterial mat; mat.init(); PxFilterData simFilterData; simFilterData.word0 = COLLISION_FLAG_OBSTACLE; simFilterData.word1 = COLLISION_FLAG_OBSTACLE_AGAINST; //simFilterData.word2 = PxPairFlag::eMODIFY_CONTACTS; PxFilterData queryFilterData; //VehicleSetupDrivableShapeQueryFilterData(&queryFilterData); createCylindricalTower(96, 3.6f, 3.6f, 14, dims, PxVec3(-10.f, -1.10f, 20.f), DefaultMaterial, mat, simFilterData, queryFilterData, 10.f); createCylindricalTower(96, 5.8f, 5.8f, 10, dims, PxVec3(-10.f, -1.10f, 20.f), DefaultMaterial, mat, simFilterData, queryFilterData, 10.f); createCylindricalTower(128, 8.f, 8.f, 8, dims, PxVec3(-10.f, -1.10f, 20.f), DefaultMaterial, mat, simFilterData, queryFilterData, 10.f); createCylindricalTower(196, 10.2f, 10.2f, 6, dims, PxVec3(-10.f, -1.10f, 20.f), DefaultMaterial, mat, simFilterData, queryFilterData, 10.f); createCylindricalTower(384, 35.f, 35.f, 6, dims, PxVec3(0.f, -1.10f, 0.f), DefaultMaterial, mat, simFilterData, queryFilterData, 10.f); /*PxFilterData planeFilterData; simFilterData.word0 = COLLISION_FLAG_GROUND; simFilterData.word1 = COLLISION_FLAG_GROUND_AGAINST; VehicleSetupDrivableShapeQueryFilterData(&queryFilterData); createGroundPlane(planeFilterData, PxVec3(0,0,0), queryFilterData); PxTransform pose = mVehicleManager.getVehicle()->getRigidDynamicActor()->getGlobalPose(); pose.p.y += 3.f; mVehicleManager.getVehicle()->getRigidDynamicActor()->setGlobalPose(pose);*/ } void SceneVehicle::setScene(PxScene* pxScene) { SceneKapla::setScene(pxScene); mVehicleManager.clearBatchQuery(); } void SceneVehicle::createStandardMaterials() { const PxF32 restitutions[MAX_NUM_SURFACE_TYPES] = { 0.2f, 0.2f, 0.2f, 0.2f }; const PxF32 staticFrictions[MAX_NUM_SURFACE_TYPES] = { 0.5f, 0.5f, 0.5f, 0.5f }; const PxF32 dynamicFrictions[MAX_NUM_SURFACE_TYPES] = { 0.5f, 0.5f, 0.5f, 0.5f }; for (PxU32 i = 0; i>= 1; if (currentSize > 0) { const PxU32 x1 = (x0 + currentSize) % initSize; const PxU32 x2 = (x0 + currentSize + currentSize) % initSize; const PxU32 y1 = (y0 + currentSize) % initSize; const PxU32 y2 = (y0 + currentSize + currentSize) % initSize; if (!done[x1 + y0*initSize]) pVB[(x1 + y0*initSize) * 9 + 1] = randomPrecomputed.getRandomInRange(-0.5f*value, 0.5f*value) + 0.5f * (pVB[(x0 + y0*initSize) * 9 + 1] + pVB[(x2 + y0*initSize) * 9 + 1]); if (!done[x0 + y1*initSize]) pVB[(x0 + y1*initSize) * 9 + 1] = randomPrecomputed.getRandomInRange(-0.5f*value, 0.5f*value) + 0.5f * (pVB[(x0 + y0*initSize) * 9 + 1] + pVB[(x0 + y2*initSize) * 9 + 1]); if (!done[x2 + y1*initSize]) pVB[(x2 + y1*initSize) * 9 + 1] = randomPrecomputed.getRandomInRange(-0.5f*value, 0.5f*value) + 0.5f * (pVB[(x2 + y0*initSize) * 9 + 1] + pVB[(x2 + y2*initSize) * 9 + 1]); if (!done[x1 + y2*initSize]) pVB[(x1 + y2*initSize) * 9 + 1] = randomPrecomputed.getRandomInRange(-0.5f*value, 0.5f*value) + 0.5f * (pVB[(x0 + y2*initSize) * 9 + 1] + pVB[(x2 + y2*initSize) * 9 + 1]); if (!done[x1 + y1*initSize]) pVB[(x1 + y1*initSize) * 9 + 1] = randomPrecomputed.getRandomInRange(-0.5f*value, 0.5f*value) + 0.5f * (pVB[(x0 + y1*initSize) * 9 + 1] + pVB[(x2 + y1*initSize) * 9 + 1]); done[x1 + y0*initSize] = true; done[x0 + y1*initSize] = true; done[x2 + y1*initSize] = true; done[x1 + y2*initSize] = true; done[x1 + y1*initSize] = true; // Recurse through 4 corners value *= 0.5f; computeTerrain(done, pVB, x0, y0, currentSize, value, initSize, randomPrecomputed); computeTerrain(done, pVB, x0, y1, currentSize, value, initSize, randomPrecomputed); computeTerrain(done, pVB, x1, y0, currentSize, value, initSize, randomPrecomputed); computeTerrain(done, pVB, x1, y1, currentSize, value, initSize, randomPrecomputed); } } void SceneVehicle::createTerrain(PxU32 size, float width, float chaos) { mNbTerrainVerts = size*size; // Vertex buffer mTerrainVB = new PxF32[(sizeof(float)*mNbTerrainVerts * 3 * 3)]; for (PxU32 y = 0; y size*0.5f && y < size*0.7f) { mTerrainVB[(x + y*size) * 9 + 1] = ay + sinf(((float)y)*12.0f + 4.0f)*2.0f; smoothBorder = false; } else { mTerrainVB[(x + y*size) * 9 + 1] = ay; } if (y > size*0.55f && y < size*0.75f) { mTerrainVB[(y + x*size) * 9 + 1] = ay + sinf(y*12.0f)*0.75f; //mTerrainVB[(y+x*size)*9+1]=ay; tagBuffer[y + x*size] = 3; tagBuffer[x + y*size] = 3; smoothBorder = false; } else if (y < size*0.15f) { const float s = size*0.15f - (float)y; mTerrainVB[(y + x*size) * 9 + 1] = ay + s*0.25f; smoothBorder = false; } else if (y > size*0.85f) { const float s = (float)y - size*0.85f; mTerrainVB[(y + x*size) * 9 + 1] = ay + s*0.7f; smoothBorder = false; } else { mTerrainVB[(y + x*size) * 9 + 1] = ay; tagBuffer[y + x*size] = 1; tagBuffer[x + y*size] = 1; } } if (smoothBorder) { mTerrainVB[((street0 - 1) + y*size) * 9 + 1] = ay*0.5f + mTerrainVB[((street0 - 1) + y*size) * 9 + 1] * 0.5f; mTerrainVB[(y + (street0 - 1)*size) * 9 + 1] = ay*0.5f + mTerrainVB[(y + (street0 - 1)*size) * 9 + 1] * 0.5f; mTerrainVB[((street0 + 1) + y*size) * 9 + 1] = ay*0.5f + mTerrainVB[((street0 + 1) + y*size) * 9 + 1] * 0.5f; mTerrainVB[(y + (street0 + 1)*size) * 9 + 1] = ay*0.5f + mTerrainVB[(y + (street0 + 1)*size) * 9 + 1] * 0.5f; } } // Circle street for (PxU32 y = 0; y= r && d < r + streetSize) { mTerrainVB[(y + x*size) * 9 + 1] = ay; if (y > size*0.55f && y < size*0.75f) tagBuffer[y + x*size] = 2; else tagBuffer[y + x*size] = 1; } else if (d >= r + streetSize && d < r + streetSize + g) { const float a = (d - (r + streetSize)) / g; mTerrainVB[(y + x*size) * 9 + 1] = ay*(1.0f - a) + mTerrainVB[(y + x*size) * 9 + 1] * a; } else if (d >= r - g && d < r) { const float a = (d - (r - g)) / g; mTerrainVB[(y + x*size) * 9 + 1] = ay*a + mTerrainVB[(y + x*size) * 9 + 1] * (1.0f - a); } } } // Borders const float b = size / 25.0f; const float bd = size / 2.0f - b; for (PxU32 y = 0; y bd || y0 > bd) { float a0 = (x0 - bd) / b; float a1 = (y0 - bd) / b; if (a1 > a0) a0 = a1; mTerrainVB[(y + x*size) * 9 + 1] = 20.0f*a0 + mTerrainVB[(y + x*size) * 9 + 1] * (1 - a0); } } } // Sobel filter for (PxU32 y = 1; y(data.mVerts); for (PxU32 i = 0; i < data.mNbVerts; i++) { verts[i] -= gChassisMeshTransform; } //Need a convex mesh for the chassis. gChassisConvexMesh = createChassisConvexMesh(data.mVerts, data.mNbVerts, getPhysics(), getCooking()); } else if (0 == Ps::strcmp(data.mName, gCarPartNames[CAR_PART_FRONT_LEFT_WHEEL])) { carPart = CAR_PART_FRONT_LEFT_WHEEL; gWheelConvexMeshes4[CAR_PART_FRONT_LEFT_WHEEL] = createWheelConvexMesh(data.mVerts, data.mNbVerts, getPhysics(), getCooking()); } else if (0 == Ps::strcmp(data.mName, gCarPartNames[CAR_PART_FRONT_RIGHT_WHEEL])) { carPart = CAR_PART_FRONT_RIGHT_WHEEL; gWheelConvexMeshes4[CAR_PART_FRONT_RIGHT_WHEEL] = createWheelConvexMesh(data.mVerts, data.mNbVerts, getPhysics(), getCooking()); } else if (0 == Ps::strcmp(data.mName, gCarPartNames[CAR_PART_REAR_LEFT_WHEEL])) { carPart = CAR_PART_REAR_LEFT_WHEEL; gWheelConvexMeshes4[CAR_PART_REAR_LEFT_WHEEL] = createWheelConvexMesh(data.mVerts, data.mNbVerts, getPhysics(), getCooking()); } else if (0 == Ps::strcmp(data.mName, gCarPartNames[CAR_PART_REAR_RIGHT_WHEEL])) { carPart = CAR_PART_REAR_RIGHT_WHEEL; gWheelConvexMeshes4[CAR_PART_REAR_RIGHT_WHEEL] = createWheelConvexMesh(data.mVerts, data.mNbVerts, getPhysics(), getCooking()); } else if (0 == Ps::strcmp(data.mName, gCarPartNames[CAR_PART_WINDOWS])) { carPart = CAR_PART_WINDOWS; //Take the offset that was required to centre the chassis and apply it to everything that is dependent on the chassis transform. PxVec3* verts = const_cast(data.mVerts); for (PxU32 i = 0; igetTransform().p; gWheelCentreOffsets4[i] = data.mTransform.p; gWheelCentreOffsets4[i].y -= gSuspensionShimHeight; //The left and right wheels seem to be mixed up. //Swap them to correct this. gWheelCentreOffsets4[i].x *= -1.0f; } } } void SceneVehicle::importRAWFile(const char* fname, PxReal scale, bool recook) { bool status = loadRAWfile(FindMediaFile(fname, mResourcePath), *this, scale); if (!status) { std::string msg = "Sample can not load file "; msg += FindMediaFile(fname, mResourcePath); msg += "\n"; printf("%s", msg.c_str()); } } void SceneVehicle::createVehicle(PxPhysics* pxPhysics) { //Initialise the sdk. mVehicleManager.init(getPhysics(), (const PxMaterial**)mStandardMaterials, mVehicleDrivableSurfaceTypes); importRAWFile("car2.raw", 1.0f); mVehicleManager.create4WVehicle(getScene(), getPhysics(), getCooking(), *mChassisMaterialDrivable, gChassisMass, gWheelCentreOffsets4, gChassisConvexMesh, gWheelConvexMeshes4, gPlayerCarStartTransforms[0], true); } //----------------------------------------------------------------------------------------------- void SceneVehicle::newMaterial(const RAWMaterial& data) { ShaderMaterial& materials = mCarPartMaterial[mNumMaterials++]; materials.init(mTextureIds[mNumMaterials - 1]); //materials.setColor(data.mDiffuseColor.x, data.mDiffuseColor.y, data.mDiffuseColor.z); materials.setColor(1, 0, 0); materials.diffuseCoeff = 0.5f; materials.specularCoeff = 0.707f; } //----------------------------------------------------------------------------------------------- void SceneVehicle::newTexture(const RAWTexture& data) { char fullPath[512]; int len = strlen(data.mName); for (PxU32 i = 0; i < len-4; ++i) { fullPath[i] = data.mName[i]; } fullPath[len - 4] = '\0'; strcat(fullPath, ".bmp"); string fName = FindMediaFile(fullPath, mResourcePath); //string fName = FindMediaFile("car02_diffuse2.bmp", mResourcePath); mTextureIds[mNumTextures++] = loadImgTexture(fName.c_str()); } // ---------------------------------------------------------------------------------------------- void SceneVehicle::handleMouseButton(int button, int state, int x, int y) { mMouseX = x; mMouseY = y; mMouseDown = (state == GLUT_DOWN); PxVec3 orig, dir; getMouseRay(x, y, orig, dir); if (glutGetModifiers() & GLUT_ACTIVE_SHIFT) { if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { if (mSimScene) mSimScene->pickStart(orig, dir); } } if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { if (mSimScene) mSimScene->pickRelease(); } } // ---------------------------------------------------------------------------------------------- void SceneVehicle::handleMouseMotion(int x, int y) { mMouseX = x; mMouseY = y; if (mSimScene && mSimScene->getPickActor() != NULL) { PxVec3 orig, dir; getMouseRay(x, y, orig, dir); mSimScene->pickMove(orig, dir); } } //ofstream orays("ray.txt"); // ---------------------------------------------------------------------------------------------- void SceneVehicle::handleKeyDown(unsigned char key, int x, int y) { if (mCameraDisable) { switch (key) { case ' ': { float vel = 1.0f; PxVec3 orig, dir; getMouseRay(mMouseX, mMouseY, orig, dir); /* float sx = 0.0f, sy = 30.0f, sz = 0.0f; int nx = 10; int ny = 10; float dx = 1.0f; for (int i = 0; i < ny; i++) { for (int j = 0; j < nx; j++) { shoot(PxVec3(sx+j*dx, sy, sz + i*dx), PxVec3(0.0f,1.0f,0.0f)); } } */ shoot(mCameraPos, dir); //orays<<"{"<toggleDebugDrawing(); break; case ' ': { mGunActive = false; } case 'w': case 'W': { mControlInputs.setAccelKeyPressed(false); break; } case 's': case 'S': { mControlInputs.setBrakeKeyPressed(false); break; } case 'a': case 'A': { mControlInputs.setSteerRightKeyPressed(false); break; } case 'd': case 'D': { mControlInputs.setSteerLeftKeyPressed(false); break; } default: { break; } } } } // ---------------------------------------------------------------------------------------------- void SceneVehicle::handleSpecialKey(unsigned char key, int x, int y) { SceneKapla::handleSpecialKey(key, x, y); switch (key) { case GLUT_KEY_F7: { mCameraDisable = !mCameraDisable; break; } } } //------------------------------------------------------------------------------------------------- void SceneVehicle::handleGamepadButton(int button, bool state) { if (mCameraDisable) { switch (button) { case SampleViewerGamepad::GAMEPAD_A: { float vel = 1.0f; PxVec3 orig, dir; getMouseRay(mMouseX, mMouseY, orig, dir); /* float sx = 0.0f, sy = 30.0f, sz = 0.0f; int nx = 10; int ny = 10; float dx = 1.0f; for (int i = 0; i < ny; i++) { for (int j = 0; j < nx; j++) { shoot(PxVec3(sx+j*dx, sy, sz + i*dx), PxVec3(0.0f,1.0f,0.0f)); } } */ shoot(mCameraPos, dir); //orays<<"{"< 0.f) { mVehicleManager.update(dt, getScene().getGravity()); } //Update the camera. mCameraController.setInputs( mControlInputs.getRotateY(), mControlInputs.getRotateZ()); mCameraController.update(dt, *mVehicleManager.getVehicle(), getScene()); setCamera(mCameraController.getCameraPos(), mCameraController.getCameraTar() - mCameraController.getCameraPos(), PxVec3(0.f, 1.f, 0.f), 40.f); } } void SceneVehicle::renderCar(bool useShader) { PxRigidDynamic* dyn = mVehicleManager.getVehicle()->getRigidDynamicActor(); PxShape* shapes[5]; dyn->getShapes(shapes, 5); PxMat44 bodyGlobalPose(dyn->getGlobalPose());// PxShapeExt::getGlobalPose(*shapes[i], *dyn)); if (useShader) mDefaultShader->activate(mCarPartMaterial[0]); for (PxU32 i = 0; i < 5; ++i) { PxMat44 globalPose(PxShapeExt::getGlobalPose(*shapes[i], *dyn)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(&globalPose.column0.x); gCarPartRenderMesh[i]->drawVBOIBO(); glPopMatrix(); } if (useShader) mDefaultShader->deactivate(); if (useShader) mDefaultShader->activate(mCarPartMaterial[1]); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(&bodyGlobalPose.column0.x); gCarPartRenderMesh[5]->drawVBOIBO(); glPopMatrix(); if (useShader) mDefaultShader->deactivate(); } void SceneVehicle::render(bool useShader) { if (createVehicleDemo) renderCar(useShader); SceneKapla::render(useShader); } PxFilterFlags VehicleFilterShader (PxFilterObjectAttributes attributes0, PxFilterData filterData0, PxFilterObjectAttributes attributes1, PxFilterData filterData1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) { PX_UNUSED(attributes0); PX_UNUSED(attributes1); PX_UNUSED(constantBlock); PX_UNUSED(constantBlockSize); if ((0 == (filterData0.word0 & filterData1.word1)) && (0 == (filterData1.word0 & filterData0.word1))) { if (filterData0.word0 != 0 && filterData1.word0 != 0) return PxFilterFlag::eSUPPRESS; } pairFlags = PxPairFlag::eCONTACT_DEFAULT; pairFlags |= PxPairFlags(PxU16(filterData0.word2 | filterData1.word2)); return PxFilterFlags(); //pairFlags = PxPairFlag::eDETECT_DISCRETE_CONTACT; //return PxFilterFlags(); } struct ActorUserData { ActorUserData() : vehicle(NULL), actor(NULL) { } const PxVehicleWheels* vehicle; const PxActor* actor; }; struct ShapeUserData { ShapeUserData() : isWheel(false), wheelId(0xffffffff) { } bool isWheel; PxU32 wheelId; }; ActorUserData gActorUserData; ShapeUserData gShapeUserDatas[PX_MAX_NB_WHEELS]; #define POINT_REJECT_ANGLE 45.0f*PxPi/180.0f #define NORMAL_REJECT_ANGLE 30.0f*PxPi/180.0f #define WHEEL_TANGENT_VELOCITY_MULTIPLIER 0.1f //The class WheelContactModifyCallback identifies and modifies rigid body contacts //that involve a wheel. Contacts that can be identified and managed by the suspension //system are ignored. Any contacts that remain are modified to account for the rotation //speed of the wheel around the rolling axis. class WheelContactModifyCallback : public PxContactModifyCallback { public: WheelContactModifyCallback() : PxContactModifyCallback() { } ~WheelContactModifyCallback(){} void onContactModify(PxContactModifyPair* const pairs, PxU32 count) { for (PxU32 i = 0; i < count; i++) { const PxRigidActor** actors = pairs[i].actor; const PxShape** shapes = pairs[i].shape; //Search for actors that represent vehicles and shapes that represent wheels. for (PxU32 j = 0; j < 2; j++) { const PxActor* actor = actors[j]; if (actor->userData && (static_cast(actor->userData))->vehicle) { const PxVehicleWheels* vehicle = (static_cast(actor->userData))->vehicle; PX_ASSERT(vehicle->getRigidDynamicActor() == actors[j]); const PxShape* shape = shapes[j]; if (shape->userData && (static_cast(shape->userData))->isWheel) { const PxU32 wheelId = (static_cast(shape->userData))->wheelId; PX_ASSERT(wheelId < vehicle->mWheelsSimData.getNbWheels()); //Modify wheel contacts. PxVehicleModifyWheelContacts(*vehicle, wheelId, WHEEL_TANGENT_VELOCITY_MULTIPLIER, 0.5f, pairs[i]); } } } } } private: }; WheelContactModifyCallback gWheelContactModifyCallback; //---------------------------------------------------------------------------------------------------- void SceneVehicle::customizeSceneDesc(PxSceneDesc& sceneDesc) { sceneDesc.filterShader = VehicleFilterShader; //Set the filter shader sceneDesc.contactModifyCallback = &gWheelContactModifyCallback; //Enable contact modification }