// // 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 "SampleSubmarine.h" #include "SampleUtils.h" #include "SampleAllocatorSDKClasses.h" #include "RenderBaseActor.h" #include "RendererMemoryMacros.h" #include "RenderMaterial.h" #include "PxTkFile.h" #include "PxPhysicsAPI.h" #include "extensions/PxExtensionsAPI.h" #include #include #include #include "SampleSubmarineInputEventIds.h" #include #include "Crab.h" #include "SubmarineCameraController.h" // for loading the HeightField #include "RenderMeshActor.h" #include "PxTkBmpLoader.h" ////////////////////////////// #include using namespace PxToolkit; using namespace SampleRenderer; using namespace SampleFramework; REGISTER_SAMPLE(SampleSubmarine, "SampleSubmarine") static PxVec3 gBuoyancy = PxVec3(0, 1.0f, 0); static PxRigidDynamic* gTreasureActor = NULL; static PxVec3 gForce = PxVec3(0, 0, 0); static PxVec3 gTorque = PxVec3(0, 0, 0); static const PxReal gLinPower = 200.0f; static const PxReal gAngPower = 5000.0f; static const PxReal gSubMarineDensity = 3.0f; static PxI32 gSubmarineHealth = 100; static PxU32 gKeyFlags = 0; static bool gTreasureFound = false; static bool gResetScene = false; static Crab* gCrab = NULL; void setupFiltering(PxRigidActor* actor, PxU32 filterGroup, PxU32 filterMask); struct Movement { enum Enum { eCRAB_FWD = (1 << 0), eCRAB_BCKWD = (1 << 1), eCRAB_ROTATE_LEFT = (1 << 2), eCRAB_ROTATE_RIGHT = (1 << 3), eSUBMAINE_FWD = (1 << 4), eSUBMAINE_BCKWD = (1 << 5), eSUBMAINE_UP = (1 << 6), eSUBMAINE_DOWN = (1 << 7), }; }; enum MaterialID { MATERIAL_TERRAIN_MUD = 1000, }; /////////////////////////////////////////////////////////////////////////////// SampleSubmarine::SampleSubmarine(PhysXSampleApplication& app) : PhysXSample(app) , mSubmarineActor(NULL) , mCameraAttachedToActor(NULL) , mSubmarineCameraController(NULL) { mCreateGroundPlane = false; //mStepperType = FIXED_STEPPER; } SampleSubmarine::~SampleSubmarine() { } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::customizeSample(SampleSetup& setup) { setup.mName = "SampleSubmarine"; } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::onInit() { mNbThreads = PxMax(PxI32(shdfnd::Thread::getNbPhysicalCores())-1, 0); mCreateCudaCtxManager = true; PhysXSample::onInit(); PxSceneWriteLock scopedLock(*mScene); mSubmarineCameraController = SAMPLE_NEW(SubmarineCameraController)(); setCameraController(mSubmarineCameraController); mApplication.setMouseCursorHiding(true); mApplication.setMouseCursorRecentering(true); mSubmarineCameraController->init(PxTransform(PxIdentity)); mSubmarineCameraController->setMouseSensitivity(0.5f); mSubmarineCameraController->setMouseLookOnMouseButton(false); getRenderer()->setAmbientColor(RendererColor(60, 60, 60)); PxMaterial& material = getDefaultMaterial(); material.setRestitution(0); material.setDynamicFriction(50.0f); material.setStaticFriction(50.0f); // set gravity getActiveScene().setGravity(PxVec3(0, -10.0f, 0)); createMaterials(); getRenderer()->setFog(SampleRenderer::RendererColor(16,16,40), 125.0f); PxRigidActor* heightField = loadTerrain("submarine_heightmap.bmp", 0.4f, 3.0f, 3.0f); if (!heightField) fatalError("Sample can not load file submarine_heightmap.bmp\n"); setupFiltering(heightField, FilterGroup::eHEIGHTFIELD, FilterGroup::eCRAB); // create ceiling plane PxReal d = 60.0f; PxTransform pose = PxTransform(PxVec3(0.0f, d, 0.0f), PxQuat(PxHalfPi, PxVec3(0.0f, 0.0f, -1.0f))); PxRigidStatic* plane = getPhysics().createRigidStatic(pose); if(!plane) fatalError("createRigidStatic failed!"); PxShape* shape = PxRigidActorExt::createExclusiveShape(*plane, PxPlaneGeometry(), material); if(!shape) fatalError("createShape failed!"); getActiveScene().addActor(*plane); resetScene(); } void SampleSubmarine::createMaterials() { RAWTexture data; data.mName = "rock_diffuse2.dds"; RenderTexture* gravelTexture = createRenderTextureFromRawTexture(data); RenderMaterial* terrainMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(), PxVec3(0.5f, 0.25f, 0.125f), 1.0f, false, MATERIAL_TERRAIN_MUD, gravelTexture); mRenderMaterials.push_back(terrainMaterial); mSeamineMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(), PxVec3(0.50f, 0.50f, 0.5f), 1.0f, false, 0xffffffff, NULL); mRenderMaterials.push_back(mSeamineMaterial); } PxRigidActor* SampleSubmarine::loadTerrain(const char* name, const PxReal heightScale, const PxReal rowScale, const PxReal columnScale) { PxRigidActor* heightFieldActor = NULL; BmpLoader loader; if(loader.loadBmp(getSampleMediaFilename(name))) { PxU16 nbColumns = PxU16(loader.mWidth), nbRows = PxU16(loader.mHeight); PxHeightFieldDesc heightFieldDesc; heightFieldDesc.nbColumns = nbColumns; heightFieldDesc.nbRows = nbRows; PxU32* samplesData = (PxU32*)SAMPLE_ALLOC(sizeof(PxU32)*nbColumns * nbRows); heightFieldDesc.samples.data = samplesData; heightFieldDesc.samples.stride = sizeof(PxU32); PxU8* currentByte = (PxU8*)heightFieldDesc.samples.data; PxU8* loader_ptr = loader.mRGB; PxVec3Alloc* vertexesA = SAMPLE_NEW(PxVec3Alloc)[nbRows * nbColumns]; PxF32* uvs = (PxF32*)SAMPLE_ALLOC(sizeof(PxF32) * nbRows * nbColumns * 2); PxVec3* vertexes = vertexesA; for (PxU32 row = 0; row < nbRows; row++) { for (PxU32 column = 0; column < nbColumns; column++) { PxHeightFieldSample* currentSample = (PxHeightFieldSample*)currentByte; currentSample->height = *loader_ptr; vertexes[row * nbColumns + column] = PxVec3(PxReal(row)*rowScale, PxReal(currentSample->height * heightScale), PxReal(column)*columnScale); uvs[(row * nbColumns + column)*2 + 0] = (float)column/7.0f; uvs[(row * nbColumns + column)*2 + 1] = (float)row/7.0f; currentSample->materialIndex0 = 0; currentSample->materialIndex1 = 0; currentSample->clearTessFlag(); currentByte += heightFieldDesc.samples.stride; loader_ptr += 3 * sizeof(PxU8); } } PxHeightField* heightField = getCooking().createHeightField(heightFieldDesc, getPhysics().getPhysicsInsertionCallback()); if(!heightField) fatalError("createHeightField failed!"); // create shape for heightfield PxTransform pose(PxVec3(-((PxReal)nbRows*rowScale) / 2.0f, 0.0f, -((PxReal)nbColumns*columnScale) / 2.0f), PxQuat(PxIdentity)); heightFieldActor = getPhysics().createRigidStatic(pose); if(!heightFieldActor) fatalError("createRigidStatic failed!"); PxShape* shape = PxRigidActorExt::createExclusiveShape(*heightFieldActor, PxHeightFieldGeometry(heightField, PxMeshGeometryFlags(), heightScale, rowScale, columnScale), getDefaultMaterial()); if(!shape) fatalError("createShape failed!"); // add actor to the scene getActiveScene().addActor(*heightFieldActor); // create indices PxU32* indices = (PxU32*)SAMPLE_ALLOC(sizeof(PxU32)*((nbColumns - 1) * (nbRows - 1) * 3 * 2)); for(int i = 0; i < (nbColumns - 1); ++i) { for(int j = 0; j < (nbRows - 1); ++j) { // first triangle indices[6 * (i * (nbRows - 1) + j) + 0] = (i + 1) * nbRows + j; indices[6 * (i * (nbRows - 1) + j) + 1] = i * nbRows + j; indices[6 * (i * (nbRows - 1) + j) + 2] = i * nbRows + j + 1; // second triangle indices[6 * (i * (nbRows - 1) + j) + 3] = (i + 1) * nbRows + j + 1; indices[6 * (i * (nbRows - 1) + j) + 4] = (i + 1) * nbRows + j; indices[6 * (i * (nbRows - 1) + j) + 5] = i * nbRows + j + 1; } } // add mesh to renderer RAWMesh data; data.mName = name; data.mTransform = PxTransform(PxIdentity); data.mNbVerts = nbColumns * nbRows; data.mVerts = vertexes; data.mVertexNormals = NULL; data.mUVs = uvs; data.mMaterialID = MATERIAL_TERRAIN_MUD; data.mNbFaces = (nbColumns - 1) * (nbRows - 1) * 2; data.mIndices = indices; RenderMeshActor* hf_mesh = createRenderMeshFromRawMesh(data); if(!hf_mesh) fatalError("createRenderMeshFromRawMesh failed!"); hf_mesh->setPhysicsShape(shape, heightFieldActor); shape->setFlag(PxShapeFlag::eVISUALIZATION, false); SAMPLE_FREE(indices); SAMPLE_FREE(uvs); DELETEARRAY(vertexesA); SAMPLE_FREE(samplesData); } return heightFieldActor; } /////////////////////////////////////////////////////////////////////////////// void setupFiltering(PxRigidActor* actor, PxU32 filterGroup, PxU32 filterMask) { PxFilterData filterData; filterData.word0 = filterGroup; // word0 = own ID filterData.word1 = filterMask; // word1 = ID mask to filter pairs that trigger a contact callback; const PxU32 numShapes = actor->getNbShapes(); PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes); actor->getShapes(shapes, numShapes); for(PxU32 i = 0; i < numShapes; i++) { PxShape* shape = shapes[i]; shape->setSimulationFilterData(filterData); } SAMPLE_FREE(shapes); } /////////////////////////////////////////////////////////////////////////////// PxRigidDynamic* SampleSubmarine::createSubmarine(const PxVec3& inPosition, const PxReal yRot) { PX_ASSERT(mSubmarineActor == NULL); std::vector localPoses; std::vector geometries; // cabin PxSphereGeometry cabinGeom(1.5f); PxTransform cabinPose = PxTransform(PxIdentity); cabinPose.p.x = -0.5f; // engine PxBoxGeometry engineGeom(0.25f, 1.0f, 1.0f); PxTransform enginePose = PxTransform(PxIdentity); enginePose.p.x = cabinPose.p.x + cabinGeom.radius + engineGeom.halfExtents.x; // tanks PxCapsuleGeometry tankGeom(0.5f, 1.8f); PxTransform tank1Pose = PxTransform(PxIdentity); tank1Pose.p = PxVec3(0,-cabinGeom.radius, cabinGeom.radius); PxTransform tank2Pose = PxTransform(PxIdentity); tank2Pose.p = PxVec3(0,-cabinGeom.radius, -cabinGeom.radius); localPoses.push_back(cabinPose); geometries.push_back(&cabinGeom); localPoses.push_back(enginePose); geometries.push_back(&engineGeom); localPoses.push_back(tank1Pose); geometries.push_back(&tankGeom); localPoses.push_back(tank2Pose); geometries.push_back(&tankGeom); // put the shapes together into one actor mSubmarineActor = PhysXSample::createCompound(inPosition, localPoses, geometries, 0, mManagedMaterials[MATERIAL_YELLOW], gSubMarineDensity)->is(); if(!mSubmarineActor) fatalError("createCompound failed!"); //disable the current and buoyancy effect for the sub. mSubmarineActor->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true); // set the filtering group for the submarine setupFiltering(mSubmarineActor, FilterGroup::eSUBMARINE, FilterGroup::eMINE_HEAD | FilterGroup::eMINE_LINK); mSubmarineActor->setLinearDamping(0.15f); mSubmarineActor->setAngularDamping(15.0f); PxTransform globalPose; globalPose.p = inPosition; globalPose.q = PxQuat(yRot, PxVec3(0,1,0)); mSubmarineActor->setGlobalPose(globalPose); mSubmarineActor->setCMassLocalPose(PxTransform(PxIdentity)); return mSubmarineActor; } /////////////////////////////////////////////////////////////////////////////// Seamine* SampleSubmarine::createSeamine(const PxVec3& inPosition, PxReal inHeight) { static const PxReal chainLinkLength = 2.0f; static const PxReal linkSpacing = 0.05f; static const PxReal mineHeadRadius = 1.5f; const PxVec3 mineStartPos = inPosition; static const PxVec3 linkOffset = PxVec3(0, chainLinkLength + linkSpacing, 0); static const PxVec3 halfLinkOffset = linkOffset * 0.5f; static const PxVec3 linkDim = PxVec3(chainLinkLength*0.125f, chainLinkLength*0.5f, chainLinkLength*0.125f); PxU32 numLinks = PxU32((inHeight - 2.0f*mineHeadRadius) / (chainLinkLength + linkSpacing)); numLinks = numLinks ? numLinks : 1; Seamine* seamine = SAMPLE_NEW(Seamine); mSeamines.push_back(seamine); // create links from floor PxVec3 linkPos = mineStartPos + halfLinkOffset; PxRigidActor* prevActor = NULL; for(PxU32 i = 0; i < numLinks; i++) { // create the link actor PxRigidDynamic* link = createBox(linkPos, linkDim, NULL, mSeamineMaterial, 1.0f)->is(); if(!link) fatalError("createBox failed!"); // back reference to mineHead link->userData = seamine; seamine->mLinks.push_back(link); setupFiltering(link, FilterGroup::eMINE_LINK, FilterGroup::eSUBMARINE); link->setLinearDamping(0.5f); link->setAngularDamping(0.5f); link->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true); // create distance joint between link and prevActor PxTransform linkFrameA = prevActor ? PxTransform(halfLinkOffset, PxQuat(PxIdentity)) : PxTransform(mineStartPos, PxQuat(PxIdentity)); PxTransform linkFrameB = PxTransform(-halfLinkOffset, PxQuat(PxIdentity)); PxDistanceJoint *joint = PxDistanceJointCreate(getPhysics(), prevActor, linkFrameA, link, linkFrameB); if(!joint) fatalError("PxDistanceJointCreate failed!"); // set min & max distance to 0 joint->setMaxDistance(0.0f); joint->setMinDistance(0.0f); // setup damping & spring joint->setDamping(1.0f * link->getMass()); joint->setStiffness(400.0f * link->getMass()); joint->setDistanceJointFlags(PxDistanceJointFlag::eMAX_DISTANCE_ENABLED | PxDistanceJointFlag::eMIN_DISTANCE_ENABLED | PxDistanceJointFlag::eSPRING_ENABLED); // add to joints array for cleanup mJoints.push_back(joint); prevActor = link; linkPos += linkOffset; } // create mine head linkPos.y += mineHeadRadius - (chainLinkLength*0.5f); PxRigidDynamic* mineHead = createSphere(linkPos, mineHeadRadius, NULL, mSeamineMaterial, 1.0f)->is(); mineHead->userData = seamine; seamine->mMineHead = mineHead; mineHead->setLinearDamping(0.5f); mineHead->setAngularDamping(0.5f); mineHead->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true); // setup filtering to trigger contact reports when submarine touches the minehead setupFiltering(mineHead, FilterGroup::eMINE_HEAD, FilterGroup::eSUBMARINE); // create distance joint between mine head and prevActor PxTransform linkFrameA = PxTransform(halfLinkOffset, PxQuat(PxIdentity)); PxTransform linkFrameB = PxTransform(PxVec3(0, -mineHeadRadius - linkSpacing*0.5f, 0), PxQuat(PxIdentity)); PxDistanceJoint *joint = PxDistanceJointCreate(getPhysics(), prevActor, linkFrameA, mineHead, linkFrameB); if(!joint) fatalError("PxDistanceJointCreate failed!"); // set min & max distance to 0 joint->setMaxDistance(0.0f); joint->setMinDistance(0.0f); // setup damping & spring joint->setDamping(1.0f * mineHead->getMass()); joint->setStiffness(400.0f * mineHead->getMass()); joint->setDistanceJointFlags(PxDistanceJointFlag::eMAX_DISTANCE_ENABLED | PxDistanceJointFlag::eMIN_DISTANCE_ENABLED | PxDistanceJointFlag::eSPRING_ENABLED); // add to joints array for cleanup mJoints.push_back(joint); return seamine; } /////////////////////////////////////////////////////////////////////////////// static const char* getPlatformName() { #if PX_X86 return "PC32"; #elif PX_X64 return "PC64"; #elif PX_ARM_FAMILY return "ARM"; #else return ""; #endif } void SampleSubmarine::createDynamicActors() { // create mines static const PxU32 numMines = 20; static const PxVec3 mineFieldCenter = PxVec3(0, 64, 0); static const PxReal minMineDistance = 3.0f; static const PxI32 mineFieldRadius = 30; for(PxU32 i = 0; i < numMines; i++) { // raycast against floor (height field) to find the height to attach the mine PxRaycastBuffer rayHit; bool hit = false; do { PxVec3 offset = PxVec3(PxReal(getSampleRandom().rand(-mineFieldRadius, mineFieldRadius)), 0, PxReal(getSampleRandom().rand(-mineFieldRadius, mineFieldRadius))); PxVec3 raycastStart = mineFieldCenter + offset*minMineDistance; hit = getActiveScene().raycast(raycastStart, PxVec3(0,-1,0), 100.0f, rayHit); } while(!hit || (rayHit.block.position.y > 25.0f) || rayHit.block.actor->is()); createSeamine(rayHit.block.position, getSampleRandom().rand(10.0f, 25.0f)); } // create treasure { static const PxVec3 treasureDim = PxVec3(5, 3, 4)*0.5f; PxRaycastBuffer rayHit; PxVec3 raycastStart = PxVec3(-19, 64, -24); getActiveScene().raycast(raycastStart, PxVec3(0,-1,0), 100.0f, rayHit); gTreasureActor = createBox(rayHit.block.position+treasureDim, treasureDim, NULL, mManagedMaterials[MATERIAL_BLUE], 1)->is(); if(!gTreasureActor) fatalError("createBox failed!"); gTreasureActor->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true); PxShape* treasureShape; gTreasureActor->getShapes(&treasureShape, 1); treasureShape->setFlag(PxShapeFlag::eSIMULATION_SHAPE, false); treasureShape->setFlag(PxShapeFlag::eTRIGGER_SHAPE, true); } // create submarine createSubmarine(PxVec3(-110, 50, 110), 2.1f); char theCrabName[256]; sprintf(theCrabName, "crab_%s.bin", getPlatformName()); // If we have already had crab copy, just load it, or will create crab and export it char thePathBuffer[1024]; const char* theCrabPath = getSampleOutputDirManager().getFilePath( theCrabName, thePathBuffer, false ); SampleFramework::File* fp = NULL; PxToolkit::fopen_s(&fp, theCrabPath, "r" ); if( fp ) { shdfnd::printFormatted("loading the crab from file status: \n"); gCrab = SAMPLE_NEW(Crab)(*this, theCrabPath, mManagedMaterials[MATERIAL_RED]); if (gCrab && !gCrab->getCrabBody()) { delete gCrab; gCrab = NULL; } shdfnd::printFormatted(gCrab ? "successful\n":"failed\n"); fclose (fp); } if( !gCrab ) { gCrab = SAMPLE_NEW(Crab)(*this, PxVec3(0, 50, 0), mManagedMaterials[MATERIAL_RED]); shdfnd::printFormatted("crab file not found ... exporting crab file\n"); gCrab->save(theCrabPath); } PX_ASSERT( gCrab ); mCrabs.push_back(gCrab); static const PxU32 numCrabs = 3; for(PxU32 i = 0; i < numCrabs; i++) { Crab* crab = SAMPLE_NEW(Crab)(*this, PxVec3(getSampleRandom().rand(-20.0f,20.0f), 50, getSampleRandom().rand(-20.0f,20.0f)), mManagedMaterials[MATERIAL_RED]); mCrabs.push_back(crab); } } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::resetScene() { gResetScene = false; const size_t nbCrabs = mCrabs.size(); for(PxU32 i=0;irelease(); mJoints.clear(); const size_t nbSeamines = mSeamines.size(); for(PxU32 i=0;imLinks.size(); for(PxU32 j=0;jmLinks[j]); removeActor(mSeamines[i]->mMineHead); delete mSeamines[i]; } mSeamines.clear(); if(mSubmarineActor) { removeActor(mSubmarineActor); mSubmarineActor = NULL; } gSubmarineHealth = 100; if (gTreasureActor) removeActor(gTreasureActor); gTreasureActor = NULL; gTreasureFound = false; while(mPhysicsActors.size()) { removeActor(mPhysicsActors[0]); } freeDeletedActors(); createDynamicActors(); // init camera orientation mSubmarineCameraController->init(PxVec3(-90, 60, 50), PxVec3(0.28f, 2.05f, 0.0f)); mSubmarineCameraController->setMouseSensitivity(0.5f); mSubmarineCameraController->setFollowingMode(true); mCameraAttachedToActor = mSubmarineActor; } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::onShutdown() { { PxSceneWriteLock scopedLock(*mScene); const size_t nbCrabs = mCrabs.size(); for(PxU32 i=0;irelease(); mJoints.clear(); const size_t nbSeamines = mSeamines.size(); for(PxU32 i=0;i forces handleInput(); // delay free crabs' memory const size_t nbDelCrabsMem = mCrabsMemoryDeleteList.size(); for(PxU32 i = 0; i < nbDelCrabsMem; i++) SAMPLE_FREE(mCrabsMemoryDeleteList[i]); mCrabsMemoryDeleteList.clear(); // change current every 0.01s static PxReal sElapsedTime = 0.0f; sElapsedTime += mPause ? 0 : dtime; if(sElapsedTime > 0.01f) { static PxReal angle = 0; angle += sElapsedTime*0.01f; angle = angle < (PxTwoPi) ? angle : angle - PxTwoPi; sElapsedTime = 0; gBuoyancy.z = 0.15f * PxSin(angle * 50); PxQuat yRot = PxQuat(angle, PxVec3(0,1,0)); gBuoyancy = yRot.rotate(gBuoyancy); // apply external forces to seamines PxSceneWriteLock scopedLock(*mScene); const size_t nbSeamines = mSeamines.size(); for(PxU32 i = 0; i < nbSeamines; i++) { Seamine* mine = mSeamines[i]; mine->mMineHead->addForce(gBuoyancy, PxForceMode::eACCELERATION); const size_t nbLinks = mine->mLinks.size(); for(PxU32 j = 0; j < nbLinks; j++) { mine->mLinks[j]->addForce(gBuoyancy, PxForceMode::eACCELERATION); } } } if(mSubmarineActor) { PxSceneWriteLock scopedLock(*mScene); //convert forces from submarine the submarine's body local space to global space PxQuat submarineOrientation = mSubmarineActor->getGlobalPose().q; gForce = submarineOrientation.rotate(gForce); gTorque = submarineOrientation.rotate(gTorque); // add also current forces to submarine gForce.z += gBuoyancy.z * 5.0f; // apply forces in global space and reset mSubmarineActor->addForce(gForce); mSubmarineActor->addTorque(gTorque); gForce = PxVec3(0); gTorque = PxVec3(0); } } void SampleSubmarine::onSubstepSetup(float dt, PxBaseTask* completionTask) { // set Crabs continuation to ensure the completion task // is not run before Crab update has completed const size_t nbCrabs = mCrabs.size(); for(PxU32 i = 0; i < nbCrabs; i++) { Crab* crab = mCrabs[i]; crab->update(dt); crab->setContinuation(completionTask); } } void SampleSubmarine::onSubstepStart(float dtime) { // kick off crab updates in parallel to simulate const size_t nbCrabs = mCrabs.size(); for(PxU32 i = 0; i < nbCrabs; i++) { Crab* crab = mCrabs[i]; // inverted stepper: skip crab updates right after creation, // crab task is not in the pipeline at this point (onSubstepSetup not yet called). if(crab->getTaskManager() == NULL) continue; crab->removeReference(); } } void SampleSubmarine::onTickPreRender(float dtime) { mScene->lockWrite(); if(gResetScene) resetScene(); // handle mine (and submarine) explosion if(mSubmarineActor) { // explode all touched mines, apply damage and force to submarine PxVec3 subMarinePos = mSubmarineActor->getGlobalPose().p; const size_t nbExplodeSeamines = mMinesToExplode.size(); for(PxU32 i=0; i < nbExplodeSeamines; i++) { Seamine* mine = mMinesToExplode[i]; PxVec3 explosionPos = mine->mMineHead->getGlobalPose().p; // disable contact trigger callback of the chain & enable gravity const size_t nbLinks = mine->mLinks.size(); for(PxU32 j = 0; j < nbLinks; j++) { PxRigidDynamic* link = mine->mLinks[j]; setupFiltering(link, 0, 0); link->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, false); } // remove mine head std::vector::iterator mineIter = std::find(mSeamines.begin(), mSeamines.end(), mine); if(mineIter != mSeamines.end()) mSeamines.erase(mineIter); removeActor(mine->mMineHead); delete mine; // give damage to submarine static const PxReal strength = 400.0f; PxVec3 explosion = subMarinePos - explosionPos; PxReal len = explosion.normalize(); PxReal damage = strength * (1.0f/len); explosion *= damage; mSubmarineActor->addForce(explosion*300); gSubmarineHealth = PxMax(gSubmarineHealth-PxI32(damage), 0); if(gSubmarineHealth == 0) { mSubmarineCameraController->init(subMarinePos - getCamera().getViewDir()*10.0f, getCamera().getRot()); explode(mSubmarineActor, subMarinePos /*- explosion*0.2f*/, damage); mCameraAttachedToActor = NULL; mSubmarineCameraController->setFollowingMode(false); mSubmarineActor = NULL; break; } } mMinesToExplode.clear(); } // respawn Crabs const size_t nbCrabs = mCrabs.size(); for(PxU32 i = 0; i < nbCrabs; i++) { Crab* crab = mCrabs[i]; if(crab->needsRespawn()) { PxRigidDynamic* prevBody = crab->getCrabBody(); PxVec3 prevPos = prevBody->getGlobalPose().p; delete crab; mCrabs[i] = SAMPLE_NEW(Crab)(*this, prevPos, mManagedMaterials[MATERIAL_RED]); if(gCrab == crab) gCrab = mCrabs[i]; if(mCameraAttachedToActor == prevBody) mCameraAttachedToActor = mCrabs[i]->getCrabBody(); } } // update camera if(mCameraAttachedToActor) mSubmarineCameraController->updateFollowingMode(getCamera(), dtime, mCameraAttachedToActor->getGlobalPose().p); mScene->unlockWrite(); // start the simulation PhysXSample::onTickPreRender(dtime); } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::explode(PxRigidActor* actor, const PxVec3& explosionPos, const PxReal explosionStrength) { size_t numRenderActors = mRenderActors.size(); for(PxU32 i = 0; i < numRenderActors; i++) { if(mRenderActors[i]->getPhysicsActor() == actor) { PxShape* shape = mRenderActors[i]->getPhysicsShape(); PxTransform pose = PxShapeExt::getGlobalPose(*shape, *actor); PxGeometryHolder geom = shape->getGeometry(); // create new actor from shape (to split compound) PxRigidDynamic* newActor = mPhysics->createRigidDynamic(pose); if(!newActor) fatalError("createRigidDynamic failed!"); PxShape* newShape = PxRigidActorExt::createExclusiveShape(*newActor, geom.any(), *mMaterial); unlink(mRenderActors[i], shape, actor); link(mRenderActors[i], newShape, newActor); newActor->setActorFlag(PxActorFlag::eVISUALIZATION, true); newActor->setLinearDamping(10.5f); newActor->setAngularDamping(0.5f); PxRigidBodyExt::updateMassAndInertia(*newActor, 1.0f); mScene->addActor(*newActor); mPhysicsActors.push_back(newActor); PxVec3 explosion = pose.p - explosionPos; PxReal len = explosion.normalize(); explosion *= (explosionStrength / len); newActor->setLinearVelocity(explosion); newActor->setAngularVelocity(PxVec3(1,2,3)); } } removeActor(actor); } /////////////////////////////////////////////////////////////////////////////// PxFilterFlags SampleSubmarineFilterShader( PxFilterObjectAttributes attributes0, PxFilterData filterData0, PxFilterObjectAttributes attributes1, PxFilterData filterData1, PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize) { // let triggers through if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1)) { pairFlags = PxPairFlag::eTRIGGER_DEFAULT; return PxFilterFlag::eDEFAULT; } // generate contacts for all that were not filtered above pairFlags = PxPairFlag::eCONTACT_DEFAULT; // trigger the contact callback for pairs (A,B) where // the filtermask of A contains the ID of B and vice versa. if((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1)) pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND; return PxFilterFlag::eDEFAULT; } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::customizeSceneDesc(PxSceneDesc& sceneDesc) { sceneDesc.filterShader = SampleSubmarineFilterShader; sceneDesc.simulationEventCallback = this; sceneDesc.flags |= PxSceneFlag::eREQUIRE_RW_LOCK; } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs) { for(PxU32 i=0; i < nbPairs; i++) { const PxContactPair& cp = pairs[i]; if(cp.events & PxPairFlag::eNOTIFY_TOUCH_FOUND) { if((pairHeader.actors[0] == mSubmarineActor) || (pairHeader.actors[1] == mSubmarineActor)) { PxActor* otherActor = (mSubmarineActor == pairHeader.actors[0]) ? pairHeader.actors[1] : pairHeader.actors[0]; Seamine* mine = reinterpret_cast(otherActor->userData); // insert only once if(std::find(mMinesToExplode.begin(), mMinesToExplode.end(), mine) == mMinesToExplode.end()) mMinesToExplode.push_back(mine); break; } } } } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::onTrigger(PxTriggerPair* pairs, PxU32 count) { for(PxU32 i=0; i < count; i++) { // ignore pairs when shapes have been deleted if (pairs[i].flags & (PxTriggerPairFlag::eREMOVED_SHAPE_TRIGGER | PxTriggerPairFlag::eREMOVED_SHAPE_OTHER)) continue; if((pairs[i].otherActor == mSubmarineActor) && (pairs[i].triggerActor == gTreasureActor)) { gTreasureFound = true; } } } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::helpRender(PxU32 x, PxU32 y, PxU8 textAlpha) { SampleRenderer::Renderer* renderer = getRenderer(); const PxU32 yInc = 18; const PxReal scale = 0.5f; const PxReal shadowOffset = 6.0f; const RendererColor textColor(255, 255, 255, textAlpha); const bool isMouseSupported = getApplication().getPlatform()->getSampleUserInput()->mouseSupported(); const bool isPadSupported = getApplication().getPlatform()->getSampleUserInput()->gamepadSupported(); const char* msg; msg = mApplication.inputInfoMsg("Press "," to toggle various debug visualization", TOGGLE_VISUALIZATION, -1); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); msg = mApplication.inputInfoMsg("Press "," to restart", SCENE_RESET,-1); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); if (isMouseSupported && isPadSupported) renderer->print(x, y += yInc, "Use mouse or right stick to rotate the camera", scale, shadowOffset, textColor); else if (isMouseSupported) renderer->print(x, y += yInc, "Use mouse to rotate the camera", scale, shadowOffset, textColor); else if (isPadSupported) renderer->print(x, y += yInc, "Use right stick to rotate the camera", scale, shadowOffset, textColor); msg = mApplication.inputInfoMsg("Press "," to switch between submarine/crab/flyCam", CAMERA_SWITCH, -1); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); if(mCameraAttachedToActor == mSubmarineActor) { renderer->print(x, y += yInc, "Submarine Controller:", scale, shadowOffset, textColor); const char* msg = mApplication.inputInfoMsg("Press "," to move along view direction", SUBMARINE_FORWARD, SUBMARINE_BACKWARD); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); msg = mApplication.inputInfoMsg("Press "," to raise and dive", SUBMARINE_UP, SUBMARINE_DOWN); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); } else if(gCrab && (mCameraAttachedToActor == gCrab->getCrabBody())) { renderer->print(x, y += yInc, "Crab Controller:", scale, shadowOffset, textColor); if (isPadSupported) renderer->print(x, y += yInc, "Use left stick to move the crab", scale, shadowOffset, textColor); const char* msg = mApplication.inputMoveInfoMsg("Press "," to move the crab", CRAB_FORWARD, CRAB_BACKWARD, CRAB_LEFT, CRAB_RIGHT); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); } else { renderer->print(x, y += yInc, "Fly Cam Controller:", scale, shadowOffset, textColor); if (isPadSupported) renderer->print(x, y += yInc, "Use left stick to move",scale, shadowOffset, textColor); const char* msg = mApplication.inputMoveInfoMsg("Press "," to move", CAMERA_MOVE_FORWARD,CAMERA_MOVE_BACKWARD, CAMERA_MOVE_LEFT, CAMERA_MOVE_RIGHT); if(msg) renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor); msg = mApplication.inputInfoMsg("Press "," to move fast", CAMERA_SHIFT_SPEED, -1); if(msg) renderer->print(x, y += yInc, msg, scale, shadowOffset, textColor); } } void SampleSubmarine::descriptionRender(PxU32 x, PxU32 y, PxU8 textAlpha) { bool print=(textAlpha!=0.0f); if(print) { Renderer* renderer = getRenderer(); const PxU32 yInc = 24; const PxReal scale = 0.5f; const PxReal shadowOffset = 6.0f; const RendererColor textColor(255, 255, 255, textAlpha); char line0[256]="This sample demonstrates the creation of jointed systems. In particular,"; char line1[256]="a complex system of driven joints is introduced to model crab motion,"; char line2[256]="while distance joints are used to model the tethering of exploding sea"; char line3[256]="mines to the seabed. Trigger shapes are presented to detect the"; char line4[256]="arrival of the submarine at a treasure chest on the ocean floor."; char line5[256]="Similarly, contact notification is used to report the overlap of the"; char line6[256]="submarine with the exploding sea mines. Crab logic is governed by sdk"; char line7[256]="raycast results, and illustrates the application of the PhysX SDK task"; char line8[256]="scheduler."; renderer->print(x, y+=yInc, line0, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line1, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line2, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line3, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line4, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line5, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line6, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line7, scale, shadowOffset, textColor); renderer->print(x, y+=yInc, line8, scale, shadowOffset, textColor); } } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::customizeRender() { SampleRenderer::Renderer* renderer = getRenderer(); const PxU32 yInc = 18; const RendererColor textColor(255, 255, 255, 255); PxU32 width, height; renderer->getWindowSize(width, height); char healthBar[20]; PxU32 h = gSubmarineHealth; sprintf(healthBar, "Health:%c%c%c%c%c%c%c%c%c%c", (h>90?'I':' '), (h>80?'I':' '), (h>70?'I':' '),(h>60?'I':' '),(h>50?'I':' '), (h>40?'I':' '), (h>30?'I':' '), (h>20?'I':' '),(h>10?'I':' '),(h>0?'I':' ')); renderer->print(width-130, height-yInc, healthBar); if(gTreasureFound) renderer->print(width-160, height-2*yInc, "Treasure Found!"); } /////////////////////////////////////////////////////////////////////////////// static void setFlag(PxU32& flags, PxU32 flag, bool set) { if(set) flags |= flag; else flags &= ~flag; } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::onPointerInputEvent(const SampleFramework::InputEvent& ie, physx::PxU32 x, physx::PxU32 y, physx::PxReal dx, physx::PxReal dy, bool val) { if(ie.m_Id== CAMERA_MOUSE_LOOK || !mSubmarineActor || mCameraAttachedToActor != mSubmarineActor) PhysXSample::onPointerInputEvent(ie,x,y,dx,dy, val); switch (ie.m_Id) { case SUBMARINE_FORWARD: { setFlag(gKeyFlags, Movement::eSUBMAINE_FWD, val); } break; case SUBMARINE_BACKWARD: { setFlag(gKeyFlags, Movement::eSUBMAINE_BCKWD, val); } break; default: break; } } ////////////////////////////////////////////////////////////////////////// void SampleSubmarine::collectInputEvents(std::vector& inputEvents) { PhysXSample::collectInputEvents(inputEvents); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_MOVE_UP); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_MOVE_DOWN); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_SPEED_INCREASE); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_SPEED_DECREASE); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_MOVE_BUTTON); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(MOUSE_LOOK_BUTTON); getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(SPAWN_DEBUG_OBJECT); //digital mouse events DIGITAL_INPUT_EVENT_DEF(SUBMARINE_FORWARD, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_LEFT, MOUSE_BUTTON_LEFT ); DIGITAL_INPUT_EVENT_DEF(SUBMARINE_BACKWARD, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_RIGHT ); //digital keyboard events DIGITAL_INPUT_EVENT_DEF(CRAB_FORWARD, SCAN_CODE_FORWARD, SCAN_CODE_FORWARD, SCAN_CODE_FORWARD ); DIGITAL_INPUT_EVENT_DEF(CRAB_BACKWARD, SCAN_CODE_BACKWARD, SCAN_CODE_BACKWARD, SCAN_CODE_BACKWARD ); DIGITAL_INPUT_EVENT_DEF(CRAB_LEFT, SCAN_CODE_LEFT, SCAN_CODE_LEFT, SCAN_CODE_LEFT ); DIGITAL_INPUT_EVENT_DEF(CRAB_RIGHT, SCAN_CODE_RIGHT, SCAN_CODE_RIGHT, SCAN_CODE_RIGHT ); DIGITAL_INPUT_EVENT_DEF(SUBMARINE_UP, SCAN_CODE_FORWARD, SCAN_CODE_FORWARD, SCAN_CODE_FORWARD ); DIGITAL_INPUT_EVENT_DEF(SUBMARINE_DOWN, SCAN_CODE_BACKWARD, SCAN_CODE_BACKWARD, SCAN_CODE_BACKWARD ); DIGITAL_INPUT_EVENT_DEF(CAMERA_SWITCH, SCAN_CODE_DOWN, SCAN_CODE_DOWN, SCAN_CODE_DOWN ); DIGITAL_INPUT_EVENT_DEF(SCENE_RESET, WKEY_R, OSXKEY_R, LINUXKEY_R ); //digital gamepad events DIGITAL_INPUT_EVENT_DEF(SUBMARINE_FORWARD, GAMEPAD_RIGHT_SHOULDER_BOT, GAMEPAD_RIGHT_SHOULDER_BOT, LINUXKEY_UNKNOWN ); DIGITAL_INPUT_EVENT_DEF(SUBMARINE_BACKWARD, GAMEPAD_LEFT_SHOULDER_BOT, GAMEPAD_LEFT_SHOULDER_BOT, LINUXKEY_UNKNOWN ); DIGITAL_INPUT_EVENT_DEF(SUBMARINE_UP, GAMEPAD_RIGHT_SHOULDER_TOP, GAMEPAD_RIGHT_SHOULDER_TOP, LINUXKEY_UNKNOWN ); DIGITAL_INPUT_EVENT_DEF(SUBMARINE_DOWN, GAMEPAD_LEFT_SHOULDER_TOP, GAMEPAD_LEFT_SHOULDER_TOP, LINUXKEY_UNKNOWN ); DIGITAL_INPUT_EVENT_DEF(CAMERA_SWITCH, GAMEPAD_RIGHT_STICK, GAMEPAD_RIGHT_STICK, LINUXKEY_UNKNOWN ); DIGITAL_INPUT_EVENT_DEF(SCENE_RESET, GAMEPAD_LEFT_STICK, GAMEPAD_LEFT_STICK, LINUXKEY_UNKNOWN ); // analog gamepad events ANALOG_INPUT_EVENT_DEF(CRAB_LEFT_RIGHT, GAMEPAD_DEFAULT_SENSITIVITY, GAMEPAD_LEFT_STICK_X, GAMEPAD_LEFT_STICK_X, LINUXKEY_UNKNOWN ); ANALOG_INPUT_EVENT_DEF(CRAB_FORWARD_BACKWARD, GAMEPAD_DEFAULT_SENSITIVITY, GAMEPAD_LEFT_STICK_Y, GAMEPAD_LEFT_STICK_Y, LINUXKEY_UNKNOWN ); ANALOG_INPUT_EVENT_DEF(SUBMARINE_FORWARD_BACKWARD, GAMEPAD_DEFAULT_SENSITIVITY, GAMEPAD_LEFT_STICK_Y, OSXKEY_UNKNOWN, LINUXKEY_UNKNOWN ); } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::onDigitalInputEvent(const SampleFramework::InputEvent& ie, bool val) { if(mSubmarineActor && mCameraAttachedToActor == mSubmarineActor) { switch (ie.m_Id) { case SUBMARINE_FORWARD: setFlag(gKeyFlags, Movement::eSUBMAINE_FWD, val); break; case SUBMARINE_BACKWARD: setFlag(gKeyFlags, Movement::eSUBMAINE_BCKWD, val); break; case SUBMARINE_UP: setFlag(gKeyFlags, Movement::eSUBMAINE_UP, val); break; case SUBMARINE_DOWN: setFlag(gKeyFlags, Movement::eSUBMAINE_DOWN, val); break; } } else if (gCrab && mCameraAttachedToActor == gCrab->getCrabBody()) { switch (ie.m_Id) { case CRAB_FORWARD: setFlag(gKeyFlags, Movement::eCRAB_FWD, val); break; case CRAB_BACKWARD: setFlag(gKeyFlags, Movement::eCRAB_BCKWD, val); break; case CRAB_LEFT: setFlag(gKeyFlags, Movement::eCRAB_ROTATE_LEFT, val); break; case CRAB_RIGHT: setFlag(gKeyFlags, Movement::eCRAB_ROTATE_RIGHT, val); break; } } if (val) { switch (ie.m_Id) { case CAMERA_SWITCH: { // cycle camera if(mSubmarineActor && mCameraAttachedToActor == NULL) mCameraAttachedToActor = mSubmarineActor; else if(gCrab && mCameraAttachedToActor != gCrab->getCrabBody()) mCameraAttachedToActor = gCrab->getCrabBody(); else mCameraAttachedToActor = NULL; mSubmarineCameraController->init(getCamera().getPos(), getCamera().getRot()); mSubmarineCameraController->setFollowingMode(mCameraAttachedToActor != NULL); } break; case SCENE_RESET: { gResetScene = true; } break; } } PhysXSample::onDigitalInputEvent(ie,val); } void SampleSubmarine::onAnalogInputEvent(const SampleFramework::InputEvent& ie, float val) { if (mSubmarineActor && mCameraAttachedToActor == mSubmarineActor) { switch (ie.m_Id) { case SUBMARINE_FORWARD_BACKWARD: { setFlag(gKeyFlags, Movement::eSUBMAINE_FWD, val > 0.3f); setFlag(gKeyFlags, Movement::eSUBMAINE_BCKWD, val < -0.3f); } break; } } if(gCrab && mCameraAttachedToActor == gCrab->getCrabBody()) { switch (ie.m_Id) { case CRAB_FORWARD_BACKWARD: { setFlag(gKeyFlags, Movement::eCRAB_FWD, val > 0.3f); setFlag(gKeyFlags, Movement::eCRAB_BCKWD, val < -0.3f); } break; case CRAB_LEFT_RIGHT: { setFlag(gKeyFlags, Movement::eCRAB_ROTATE_LEFT, val > 0.3f); setFlag(gKeyFlags, Movement::eCRAB_ROTATE_RIGHT, val < -0.3f); } break; } } } /////////////////////////////////////////////////////////////////////////////// void SampleSubmarine::handleInput() { if(gCrab && mCameraAttachedToActor == gCrab->getCrabBody()) { PxReal accFactor = 3.0f; PxReal forward = 0, rot = 0; if(gKeyFlags & Movement::eCRAB_FWD) forward = 1.0f; if(gKeyFlags & Movement::eCRAB_BCKWD) forward = -1.0f; if(gKeyFlags & Movement::eCRAB_ROTATE_LEFT) rot = 1.0f; if(gKeyFlags & Movement::eCRAB_ROTATE_RIGHT) rot = -1.0f; PxReal left = rot + forward; PxReal right = -rot + forward; gCrab->setAcceleration(left*accFactor, right*accFactor); } else if(mSubmarineActor && mCameraAttachedToActor == mSubmarineActor) { if(gKeyFlags & Movement::eSUBMAINE_FWD) gForce.x -= gLinPower; if(gKeyFlags & Movement::eSUBMAINE_BCKWD) gForce.x += gLinPower; if(gKeyFlags & Movement::eSUBMAINE_UP) gForce.y += gLinPower; if(gKeyFlags & Movement::eSUBMAINE_DOWN) gForce.y -= gLinPower; if(gKeyFlags & (Movement::eSUBMAINE_FWD|Movement::eSUBMAINE_BCKWD)) { PxSceneReadLock scopedLock(*mScene); static const PxReal camEpsilon = 0.001f; PxTransform subPose = mSubmarineActor->getGlobalPose(); PxVec3 cameraDir = getCamera().getViewDir(); PxVec3 cameraDirInSub = subPose.rotateInv(cameraDir).getNormalized(); PxVec3 cameraUpInSub = subPose.rotateInv(PxVec3(0,1,0)).getNormalized(); if(PxAbs(cameraDirInSub.z) > camEpsilon) gTorque.y += gAngPower*cameraDirInSub.z; if(PxAbs(cameraDirInSub.y) > camEpsilon) gTorque.z -= gAngPower*cameraDirInSub.y; if(PxAbs(cameraUpInSub.z) > camEpsilon) gTorque.x += gAngPower*cameraUpInSub.z; } } } //////////////////////////////////////////////////////////////////////////