// // 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. #include "PxPhysXConfig.h" #include "foundation/PxMemory.h" #include "SamplePreprocessor.h" #include "PhysXSampleApplication.h" #include "PhysXSample.h" #include "SampleCommandLine.h" #include "PxTkFile.h" #include "PsString.h" #include "PsFPU.h" #include "PxToolkit.h" #include "extensions/PxDefaultStreams.h" #include "RenderBoxActor.h" #include "RenderSphereActor.h" #include "RenderCapsuleActor.h" #include "RenderMeshActor.h" #include "RenderGridActor.h" #include "RenderMaterial.h" #include "RenderTexture.h" #include "RenderPhysX3Debug.h" #include #include "SampleBaseInputEventIds.h" #include #include "SampleUserInputDefines.h" #include #include "SampleInputMappingAsset.h" #include #include #include "Picking.h" #include "TestGroup.h" #if PX_WINDOWS // Starting with Release 302 drivers, application developers can direct the Optimus driver at runtime to use the High Performance // Graphics to render any application; even those applications for which there is no existing application profile. // They can do this by exporting a global variable named PxOptimusEnablement. // A value of 0x00000001 indicates that rendering should be performed using High Performance Graphics. // A value of 0x00000000 indicates that this method should be ignored. extern "C" { _declspec(dllexport) DWORD PxOptimusEnablement = 0x00000001; } #endif using namespace SampleFramework; using namespace SampleRenderer; static bool gRecook = false; PxDefaultAllocator gDefaultAllocatorCallback; enum MaterialID { MATERIAL_CLOTH = 444, }; /////////////////////////////////////////////////////////////////////////////// PX_FORCE_INLINE PxSimulationFilterShader getSampleFilterShader() { return PxDefaultSimulationFilterShader; } /////////////////////////////////////////////////////////////////////////////// PX_FORCE_INLINE void SetupDefaultRigidDynamic(PxRigidDynamic& body, bool kinematic=false) { body.setActorFlag(PxActorFlag::eVISUALIZATION, true); body.setAngularDamping(0.5f); body.setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, kinematic); } void PhysXSample::unlink(RenderBaseActor* renderActor, PxShape* shape, PxRigidActor* actor) { PhysXShape theShape(actor, shape); mPhysXShapeToRenderActorMap.erase(theShape); PX_UNUSED(renderActor); } void PhysXSample::link(RenderBaseActor* renderActor, PxShape* shape, PxRigidActor* actor) { PhysXShape theShape(actor, shape); mPhysXShapeToRenderActorMap[theShape] = renderActor; renderActor->setPhysicsShape(shape, actor); } RenderBaseActor* PhysXSample::getRenderActor(PxRigidActor* actor, PxShape* shape) { PhysXShape theShape(actor, shape); PhysXShapeToRenderActorMap::iterator it = mPhysXShapeToRenderActorMap.find(theShape); if (mPhysXShapeToRenderActorMap.end() != it) return it->second; return NULL; } SampleDirManager& PhysXSample::getSampleOutputDirManager() { static SampleDirManager gSampleOutputDirManager(SAMPLE_OUTPUT_PATH, false); return gSampleOutputDirManager; } PxToolkit::BasicRandom& getSampleRandom() { static PxToolkit::BasicRandom gRandom(42); return gRandom; } PxErrorCallback& getSampleErrorCallback() { static PxDefaultErrorCallback gDefaultErrorCallback; return gDefaultErrorCallback; } // sschirm: same here: would be good to have a place for platform independent path manipulation // shared for all apps const char* getFilenameFromPath(const char* filePath, char* buffer) { const char* ptr = strrchr(filePath, '/'); if (!ptr) ptr = strrchr(filePath, '\\'); if (ptr) { strcpy(buffer, ptr + 1); } else { strcpy(buffer, filePath); } return buffer; } const char* PhysXSample::getSampleOutputFilePath(const char* inFilePath, const char* outExtension) { static char sBuffer[1024]; char tmpBuffer[1024]; const char* inFilename = getFilenameFromPath(inFilePath, sBuffer); sprintf(tmpBuffer, "cached/%s%s", inFilename, outExtension); return getSampleOutputDirManager().getFilePath(tmpBuffer, sBuffer, false); } static void GenerateCirclePts(unsigned int nb, PxVec3* pts, float scale, float z) { for(unsigned int i=0;i 50) { for(int i=0;i& poses, const std::vector& geometries, bool kinematic=false, PxReal density = 1.0f) { PxRigidDynamic* actor = sdk.createRigidDynamic(PxTransform(pos, rot)); SetupDefaultRigidDynamic(*actor); PX_ASSERT(poses.size() == geometries.size()); for(PxU32 i=0;isetLocalPose(currentPose); PX_ASSERT(shape); } if(actor) { PxRigidBodyExt::updateMassAndInertia(*actor, density); scene->addActor(*actor); } return actor; } ////////////////////////////////////////////////////////////////////////// void PhysXSample::onRelease(const PxBase* observed, void* userData, PxDeletionEventFlag::Enum deletionEvent) { PX_UNUSED(userData); PX_UNUSED(deletionEvent); if(observed->is()) { const PxRigidActor* actor = static_cast(observed); removeRenderActorsFromPhysicsActor(actor); std::vector::iterator actorIter = std::find(mPhysicsActors.begin(), mPhysicsActors.end(), actor); if(actorIter != mPhysicsActors.end()) { mPhysicsActors.erase(actorIter); } } } /////////////////////////////////////////////////////////////////////////////// RenderMeshActor* PhysXSample::createRenderMeshFromRawMesh(const RAWMesh& data, PxShape* shape) { // Create render mesh from raw mesh const PxU32 nbTris = data.mNbFaces; const PxU32* src = data.mIndices; PxU16* indices = (PxU16*)SAMPLE_ALLOC(sizeof(PxU16)*nbTris*3); for(PxU32 i=0;imID==data.mMaterialID) { meshActor->setRenderMaterial(mRenderMaterials[i]); break; } } } meshActor->setTransform(data.mTransform); if(data.mName) strcpy(meshActor->mName, data.mName); mRenderActors.push_back(meshActor); return meshActor; } /////////////////////////////////////////////////////////////////////////////// RenderTexture* PhysXSample::createRenderTextureFromRawTexture(const RAWTexture& data) { RenderTexture* texture; if(!data.mPixels) { // PT: the pixel data is not included in the RAW file so we use the asset manager to load the texture SampleAsset* t = getAsset(getSampleMediaFilename(data.mName), SampleAsset::ASSET_TEXTURE); PX_ASSERT(t->getType()==SampleAsset::ASSET_TEXTURE); mManagedAssets.push_back(t); SampleTextureAsset* textureAsset = static_cast(t); texture = SAMPLE_NEW(RenderTexture)(*getRenderer(), data.mID, textureAsset->getTexture()); } else { // PT: the pixel data is directly included in the RAW file texture = SAMPLE_NEW(RenderTexture)(*getRenderer(), data.mID, data.mWidth, data.mHeight, data.mPixels); } if(data.mName) strcpy(texture->mName, data.mName); mRenderTextures.push_back(texture); return texture; } /////////////////////////////////////////////////////////////////////////////// void PhysXSample::newMaterial(const RAWMaterial& data) { RenderTexture* diffuse = NULL; if(data.mDiffuseID!=0xffffffff) { size_t nbTextures = mRenderTextures.size(); for(PxU32 i=0;imID==data.mDiffuseID) { diffuse = mRenderTextures[i]; break; } } } RenderMaterial* newRM = SAMPLE_NEW(RenderMaterial)(*getRenderer(), data.mDiffuseColor, data.mOpacity, data.mDoubleSided, data.mID, diffuse); // strcpy(newRM->mName, data.mName); mRenderMaterials.push_back(newRM); } void PhysXSample::newMesh(const RAWMesh& data) { // PT: the mesh name should capture the scale value as well, to make sure different scales give birth to different cooked files const PxU32 scaleTag = PX_IR(mScale); PX_ASSERT(mFilename); char extension[256]; sprintf(extension, "_%d_%x.cooked", mMeshTag, scaleTag); const char* filePathCooked = getSampleOutputFilePath(mFilename, extension); PX_ASSERT(NULL != filePathCooked); bool ok = false; if(!gRecook) { SampleFramework::File* fp = NULL; PxToolkit::fopen_s(&fp, filePathCooked, "rb"); if(fp) { fclose(fp); ok = true; } } if(!ok) { PxTriangleMeshDesc meshDesc; meshDesc.points.count = data.mNbVerts; meshDesc.triangles.count = data.mNbFaces; meshDesc.points.stride = 4*3; meshDesc.triangles.stride = 4*3; meshDesc.points.data = data.mVerts; meshDesc.triangles.data = data.mIndices; // shdfnd::printFormatted("Cooking object... %s",filePathCooked); PxDefaultFileOutputStream stream(filePathCooked); ok = mCooking->cookTriangleMesh(meshDesc, stream); shdfnd::printFormatted(" - Done\n"); } if(ok) { PxDefaultFileInputData stream(filePathCooked); PxTriangleMesh* triangleMesh = mPhysics->createTriangleMesh(stream); if(triangleMesh) { PxTriangleMeshGeometry triGeom; triGeom.triangleMesh = triangleMesh; PxRigidStatic* actor = mPhysics->createRigidStatic(data.mTransform); PxShape* shape = PxRigidActorExt::createExclusiveShape(*actor, triGeom, *mMaterial); PX_UNUSED(shape); mScene->addActor(*actor); addPhysicsActors(actor); if(0) { // Create render mesh from PhysX mesh PxU32 nbVerts = triangleMesh->getNbVertices(); const PxVec3* verts = triangleMesh->getVertices(); PxU32 nbTris = triangleMesh->getNbTriangles(); const void* tris = triangleMesh->getTriangles(); bool s = triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; PX_ASSERT(s); PX_UNUSED(s); const PxU16* src = (const PxU16*)tris; PxU16* indices = (PxU16*)SAMPLE_ALLOC(sizeof(PxU16)*nbTris*3); for(PxU32 i=0;imName, data.mName); PxShape* shape; actor->getShapes(&shape, 1); link(meshActor, shape, actor); mRenderActors.push_back(meshActor); meshActor->setEnableCameraCull(true); SAMPLE_FREE(indices); } else { PxShape* shape; actor->getShapes(&shape, 1); RenderMeshActor* meshActor = createRenderMeshFromRawMesh(data, shape); link(meshActor, shape, actor); meshActor->setEnableCameraCull(true); } mMeshTag++; } } } void PhysXSample::newShape(const RAWShape&) { } void PhysXSample::newHelper(const RAWHelper&) { } void PhysXSample::newTexture(const RAWTexture& data) { createRenderTextureFromRawTexture(data); } /////////////////////////////////////////////////////////////////////////////// void PhysXSample::togglePvdConnection() { if(mPvd == NULL) return; if (mPvd->isConnected()) mPvd->disconnect(); else mPvd->connect(*mTransport,mPvdFlags); } void PhysXSample::createPvdConnection() { #if PX_SUPPORT_PVD //Create a pvd connection that writes data straight to the filesystem. This is //the fastest connection on windows for various reasons. First, the transport is quite fast as //pvd writes data in blocks and filesystems work well with that abstraction. //Second, you don't have the PVD application parsing data and using CPU and memory bandwidth //while your application is running. //physx::PxPvdTransport* transport = physx::PxDefaultPvdFileTransportCreate( "c:\\mywork\\sample.pxd2" ); //The normal way to connect to pvd. PVD needs to be running at the time this function is called. //We don't worry about the return value because we are already registered as a listener for connections //and thus our onPvdConnected call will take care of setting up our basic connection state. mTransport = physx::PxDefaultPvdSocketTransportCreate(mPvdParams.ip, mPvdParams.port, mPvdParams.timeout); if(mTransport == NULL) return; //The connection flags state overall what data is to be sent to PVD. Currently //the Debug connection flag requires support from the implementation (don't send //the data when debug isn't set) but the other two flags, profile and memory //are taken care of by the PVD SDK. //Use these flags for a clean profile trace with minimal overhead mPvdFlags = physx::PxPvdInstrumentationFlag::eALL; if (!mPvdParams.useFullPvdConnection ) { mPvdFlags = physx::PxPvdInstrumentationFlag::ePROFILE; } mPvd = physx::PxCreatePvd( *mFoundation ); mPvd->connect(*mTransport,mPvdFlags); #endif } /////////////////////////////////////////////////////////////////////////////// // // default implemententions for PhysXSample interface // void PhysXSample::onPointerInputEvent(const InputEvent& ie, physx::PxU32 x, physx::PxU32 y, physx::PxReal dx, physx::PxReal dy, bool pressed) { switch (ie.m_Id) { case CAMERA_MOUSE_LOOK: { if(mPicking) mPicking->moveCursor(x,y); } break; case PICKUP: { if(mPicking) { mPicked = !mPicked;; if(mPicked) mPicking->lazyPick(); else mPicking->letGo(); } } break; } } void PhysXSample::onResize(PxU32 width, PxU32 height) { mApplication.baseResize(width, height); } PhysXSample::~PhysXSample() { for (size_t i = mDeletedRenderActors.size(); i--;) { mDeletedRenderActors[i]->release(); } mDeletedRenderActors.clear(); PX_ASSERT(!mRenderActors.size()); PX_ASSERT(!mRenderTextures.size()); PX_ASSERT(!mRenderMaterials.size()); PX_ASSERT(!mPhysicsActors.size()); PX_ASSERT(!mManagedAssets.size()); PX_ASSERT(!mBufferedActiveActors); PX_ASSERT(!mScene); PX_ASSERT(!mCpuDispatcher); // PX_ASSERT(!mMaterial); #if PX_SUPPORT_GPU_PHYSX PX_ASSERT(!mCudaContextManager); #endif PX_ASSERT(!mCooking); PX_ASSERT(!mPhysics); PX_ASSERT(!mFoundation); if(SCRATCH_BLOCK_SIZE) SAMPLE_FREE(mScratchBlock); delete mPicking; } PhysXSample::PhysXSample(PhysXSampleApplication& app, PxU32 maxSubSteps) : mInitialDebugRender(false), mCreateCudaCtxManager(true), mCreateGroundPlane(true), mStepperType(FIXED_STEPPER), mMaxNumSubSteps(maxSubSteps), mNbThreads(1), mDefaultDensity(20.0f), mDisplayFPS(true), mPause(app.mPause), mOneFrameUpdate(app.mOneFrameUpdate), mShowHelp(app.mShowHelp), mShowDescription(app.mShowDescription), mShowExtendedHelp(app.mShowExtendedHelp), mHideGraphics(false), mEnableAutoFlyCamera(false), mCameraController(app.mCameraController), mPvdParams(app.mPvdParams), mApplication(app), mFoundation(NULL), mPhysics(NULL), mCooking(NULL), mScene(NULL), mMaterial(NULL), mCpuDispatcher(NULL), mPvd(NULL), mTransport(NULL), #if PX_SUPPORT_GPU_PHYSX mCudaContextManager(NULL), #endif mManagedMaterials(app.mManagedMaterials), mSampleInputAsset(NULL), mBufferedActiveActors(NULL), mActiveTransformCount(0), mActiveTransformCapacity(0), mIsFlyCamera(false), mMeshTag(0), mFilename(NULL), mScale(1.0f), mDebugRenderScale(1.0f), mWaitForResults(false), mSavedCameraController(NULL), mDebugStepper(0.016666660f), mFixedStepper(0.016666660f, maxSubSteps), mVariableStepper(1.0f / 80.0f, 1.0f / 40.0f, maxSubSteps), mWireFrame(false), mSimulationTime(0.0f), mPicked(false), mExtendedHelpPage(0), mDebugObjectType(DEBUG_OBJECT_BOX)//, { mDebugStepper.setSample(this); mFixedStepper.setSample(this); mVariableStepper.setSample(this); mScratchBlock = SCRATCH_BLOCK_SIZE ? SAMPLE_ALLOC(SCRATCH_BLOCK_SIZE) : 0; mFlyCameraController.init(PxVec3(0.0f, 10.0f, 0.0f), PxVec3(0.0f, 0.0f, 0.0f)); mPicking = new physx::Picking(*this); mDeletedActors.clear(); } void PhysXSample::render() { PxU32 nbVisible = 0; if(!mHideGraphics) { PX_PROFILE_ZONE("Renderer.CullObjects", 0); Camera& camera = getCamera(); Renderer* renderer = getRenderer(); for(PxU32 i = 0; i < mRenderActors.size(); ++i) { RenderBaseActor* renderActor = mRenderActors[i]; if(camera.mPerformVFC && renderActor->getEnableCameraCull() && getCamera().cull(renderActor->getWorldBounds())==PLANEAABB_EXCLUSION) continue; renderActor->render(*renderer, mManagedMaterials[MATERIAL_GREY], mWireFrame); ++nbVisible; } //if(camera.mPerformVFC) //shdfnd::printFormatted("Nb visible: %d\n", nbVisible); } RenderPhysX3Debug* debugRender = getDebugRenderer(); if(debugRender) debugRender->queueForRenderTriangle(); } void PhysXSample::displayFPS() { if(!mDisplayFPS) return; char fpsText[512]; sprintf(fpsText, "%0.2f fps", mFPS.getFPS()); Renderer* renderer = getRenderer(); const PxU32 yInc = 18; renderer->print(10, getCamera().getScreenHeight() - yInc*2, fpsText); // sprintf(fpsText, "%d visible objects", mNbVisible); // renderer->print(10, mCamera.getScreenHeight() - yInc*3, fpsText); } void PhysXSample::onShutdown() { //mScene->fetchResults(true); releaseAll(mRenderActors); releaseAll(mRenderTextures); releaseAll(mRenderMaterials); { PxSceneWriteLock scopedLock(*mScene); releaseAll(mPhysicsActors); } SAMPLE_FREE(mBufferedActiveActors); mFixedStepper.shutdown(); mDebugStepper.shutdown(); mVariableStepper.shutdown(); const size_t nbManagedAssets = mManagedAssets.size(); if(nbManagedAssets) { SampleAssetManager* assetManager = mApplication.getAssetManager(); for(PxU32 i=0; ireturnAsset(*mManagedAssets[i]); } mManagedAssets.clear(); mApplication.getPlatform()->getSampleUserInput()->shutdown(); if(mSampleInputAsset) { mApplication.getAssetManager()->returnAsset(*mSampleInputAsset); mSampleInputAsset = NULL; } mPhysics->unregisterDeletionListener(*this); SAFE_RELEASE(mScene); SAFE_RELEASE(mCpuDispatcher); // SAFE_RELEASE(mMaterial); SAFE_RELEASE(mCooking); PxCloseExtensions(); SAFE_RELEASE(mPhysics); #if PX_SUPPORT_GPU_PHYSX SAFE_RELEASE(mCudaContextManager); #endif SAFE_RELEASE(mPvd); SAFE_RELEASE(mTransport); SAFE_RELEASE(mFoundation); } //#define USE_MBP #ifdef USE_MBP static void setupMBP(PxScene& scene) { const float range = 1000.0f; const PxU32 subdiv = 4; // const PxU32 subdiv = 1; // const PxU32 subdiv = 2; // const PxU32 subdiv = 8; const PxVec3 min(-range); const PxVec3 max(range); const PxBounds3 globalBounds(min, max); PxBounds3 bounds[256]; const PxU32 nbRegions = PxBroadPhaseExt::createRegionsFromWorldBounds(bounds, globalBounds, subdiv); for(PxU32 i=0;igetDriverType()) { case Renderer::DRIVER_DIRECT3D11: cudaContextManagerDesc.interopMode = PxCudaInteropMode::D3D11_INTEROP; break; case Renderer::DRIVER_OPENGL: cudaContextManagerDesc.interopMode = PxCudaInteropMode::OGL_INTEROP; break; } cudaContextManagerDesc.graphicsDevice = getRenderer()->getDevice(); } #endif mCudaContextManager = PxCreateCudaContextManager(*mFoundation, cudaContextManagerDesc, PxGetProfilerCallback()); if( mCudaContextManager ) { if( !mCudaContextManager->contextIsValid() ) { mCudaContextManager->release(); mCudaContextManager = NULL; } } } #endif createPvdConnection(); PxTolerancesScale scale; customizeTolerances(scale); mPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *mFoundation, scale, recordMemoryAllocations, mPvd); if(!mPhysics) fatalError("PxCreatePhysics failed!"); if(!PxInitExtensions(*mPhysics, mPvd)) fatalError("PxInitExtensions failed!"); PxCookingParams params(scale); params.meshWeldTolerance = 0.001f; params.meshPreprocessParams = PxMeshPreprocessingFlags(PxMeshPreprocessingFlag::eWELD_VERTICES); params.buildGPUData = true; //Enable GRB data being produced in cooking. mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, params); if(!mCooking) fatalError("PxCreateCooking failed!"); mPhysics->registerDeletionListener(*this, PxDeletionEventFlag::eUSER_RELEASE); // setup default material... mMaterial = mPhysics->createMaterial(0.5f, 0.5f, 0.1f); if(!mMaterial) fatalError("createMaterial failed!"); PX_ASSERT(NULL == mScene); PxSceneDesc sceneDesc(mPhysics->getTolerancesScale()); sceneDesc.gravity = PxVec3(0.0f, -9.81f, 0.0f); getDefaultSceneDesc(sceneDesc); if(!sceneDesc.cpuDispatcher) { mCpuDispatcher = PxDefaultCpuDispatcherCreate(mNbThreads); if(!mCpuDispatcher) fatalError("PxDefaultCpuDispatcherCreate failed!"); sceneDesc.cpuDispatcher = mCpuDispatcher; } if(!sceneDesc.filterShader) sceneDesc.filterShader = getSampleFilterShader(); #if PX_SUPPORT_GPU_PHYSX if(!sceneDesc.cudaContextManager) sceneDesc.cudaContextManager = mCudaContextManager; #endif //sceneDesc.frictionType = PxFrictionType::eTWO_DIRECTIONAL; //sceneDesc.frictionType = PxFrictionType::eONE_DIRECTIONAL; //sceneDesc.flags |= PxSceneFlag::eENABLE_GPU_DYNAMICS; sceneDesc.flags |= PxSceneFlag::eENABLE_PCM; //sceneDesc.flags |= PxSceneFlag::eENABLE_AVERAGE_POINT; sceneDesc.flags |= PxSceneFlag::eENABLE_STABILIZATION; //sceneDesc.flags |= PxSceneFlag::eADAPTIVE_FORCE; sceneDesc.flags |= PxSceneFlag::eENABLE_ACTIVE_ACTORS; sceneDesc.sceneQueryUpdateMode = PxSceneQueryUpdateMode::eBUILD_ENABLED_COMMIT_DISABLED; //sceneDesc.flags |= PxSceneFlag::eDISABLE_CONTACT_CACHE; //sceneDesc.broadPhaseType = PxBroadPhaseType::eGPU; //sceneDesc.broadPhaseType = PxBroadPhaseType::eSAP; sceneDesc.gpuMaxNumPartitions = 8; //sceneDesc.solverType = PxSolverType::eTGS; #ifdef USE_MBP sceneDesc.broadPhaseType = PxBroadPhaseType::eMBP; #endif customizeSceneDesc(sceneDesc); mScene = mPhysics->createScene(sceneDesc); if(!mScene) fatalError("createScene failed!"); PxSceneWriteLock scopedLock(*mScene); PxSceneFlags flag = mScene->getFlags(); PX_UNUSED(flag); mScene->setVisualizationParameter(PxVisualizationParameter::eSCALE, mInitialDebugRender ? mDebugRenderScale : 0.0f); mScene->setVisualizationParameter(PxVisualizationParameter::eCOLLISION_SHAPES, 1.0f); PxPvdSceneClient* pvdClient = mScene->getScenePvdClient(); if(pvdClient) { pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONSTRAINTS, true); pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true); pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_SCENEQUERIES, true); } #ifdef USE_MBP setupMBP(*mScene); #endif mApplication.refreshVisualizationMenuState(PxVisualizationParameter::eCOLLISION_SHAPES); mApplication.applyDefaultVisualizationSettings(); mApplication.setMouseCursorHiding(false); mApplication.setMouseCursorRecentering(false); mCameraController.setMouseLookOnMouseButton(false); mCameraController.setMouseSensitivity(1.0f); if(mCreateGroundPlane) createGrid(); LOG_INFO("PhysXSample", "Init sample ok!"); } RenderMaterial* PhysXSample::getMaterial(PxU32 materialID) { for(PxU32 i = 0; i < mRenderMaterials.size(); ++i) { if(mRenderMaterials[i]->mID == materialID) { return mRenderMaterials[i]; } } return NULL; } Stepper* PhysXSample::getStepper() { switch(mStepperType) { case DEFAULT_STEPPER: return &mDebugStepper; case FIXED_STEPPER: return &mFixedStepper; default: return &mVariableStepper; }; } // ### PT: TODO: refactor this with onTickPreRender void PhysXSample::freeDeletedActors() { getRenderer()->finishRendering(); // delete buffered render actors for (size_t i = mDeletedRenderActors.size(); i--;) { mDeletedRenderActors[i]->release(); } mDeletedRenderActors.clear(); } void PhysXSample::onTickPreRender(float dtime) { // delete buffered render actors for (size_t i = mDeletedRenderActors.size(); i--;) { mDeletedRenderActors[i]->release(); } mDeletedRenderActors.clear(); #if PX_PROFILE { #endif PxSceneWriteLock scopedLock(*mScene); mApplication.baseTickPreRender(dtime); mFPS.update(); #if PX_PROFILE } #endif if(!isPaused()) { Stepper* stepper = getStepper(); mWaitForResults = false; if(mScene) { updateRenderObjectsAsync(dtime); #if !PX_PROFILE mWaitForResults = stepper->advance(mScene, dtime, mScratchBlock, SCRATCH_BLOCK_SIZE); // tells the stepper shape data is not going to be accessed until next frame // (frame ends with stepper->wait(mScene)) stepper->renderDone(); #else // in profile builds we run the whole frame sequentially // simulate, wait, update render objects, render { mWaitForResults = stepper->advance(mScene, dtime, mScratchBlock, SCRATCH_BLOCK_SIZE); stepper->renderDone(); if (mWaitForResults) { stepper->wait(mScene); mSimulationTime = stepper->getSimulationTime(); } } // update render objects immediately if (mWaitForResults) { bufferActiveTransforms(); updateRenderObjectsSync(dtime); if (mOneFrameUpdate) updateRenderObjectsAsync(dtime); } #endif } } // profile builds should update render actors // and debug draw immediately to avoid one frame lag #if PX_PROFILE RenderPhysX3Debug* debugRenderer = getDebugRenderer(); if(debugRenderer && mScene) { const PxRenderBuffer& debugRenderable = mScene->getRenderBuffer(); debugRenderer->update(debugRenderable); updateRenderObjectsDebug(dtime); } #endif if(mPicking) { mPicking->tick(); } } void PhysXSample::onTickPostRender(float dtime) { #if !PX_PROFILE if(!isPaused()) { if(mScene && mWaitForResults) { Stepper* stepper = getStepper(); stepper->wait(mScene); mSimulationTime = stepper->getSimulationTime(); bufferActiveTransforms(); // make sure that in single step mode, the render objects get updated immediately if (mOneFrameUpdate) { updateRenderObjectsSync(dtime); updateRenderObjectsAsync(dtime); } else updateRenderObjectsSync(dtime); } } #else if(!isPaused() && mScene && mWaitForResults ) { Stepper* stepper = getStepper(); //stepper->wait(mScene); stepper->postRender(dtime); } #endif RenderPhysX3Debug* debugRenderer = getDebugRenderer(); if(debugRenderer && mScene) { const PxRenderBuffer& debugRenderable = mScene->getRenderBuffer(); PX_UNUSED(debugRenderable); debugRenderer->clear(); #if !PX_PROFILE debugRenderer->update(debugRenderable); updateRenderObjectsDebug(dtime); #endif renderScene(); } if(mOneFrameUpdate) { mOneFrameUpdate = false; if (!isPaused()) togglePause(); } #ifdef PRINT_BLOCK_COUNTERS static PxU32 refMax = -1; PxU32 newMax = getActiveScene().getMaxNbContactDataBlocksUsed(); if(refMax != newMax) shdfnd::printFormatted("max blocks used: %d\n",newMax); refMax = newMax; #endif // PRINT_BLOCK_COUNTERS } void PhysXSample::saveUserInputs() { char name[256]; char sBuffer[512]; const char* sampleName = mApplication.mRunning->getName(); SampleUserInput* sampleUserInput = mApplication.getPlatform()->getSampleUserInput(); if(!sampleUserInput) return; strcpy(name,"input"); strcat(name,"/UserInputList.txt"); if(getSampleOutputDirManager().getFilePath(name, sBuffer, false)) { sprintf(name, "input/%s/%sUserInputList.txt", sampleName,mApplication.getPlatform()->getPlatformName()); if(getSampleOutputDirManager().getFilePath(name, sBuffer, false)) { SampleFramework::File* file = NULL; PxToolkit::fopen_s(&file, sBuffer, "w"); if(file) { fputs("UserInputList:\n",file); fputs("----------------------------------------\n",file); const std::vector& ilist = sampleUserInput->getUserInputList(); for (size_t i = 0; i < ilist.size(); i++) { const UserInput& ui = ilist[i]; fputs(ui.m_IdName,file); fputs("\n",file); } fclose(file); } } } } void PhysXSample::saveInputEvents(const std::vector& inputEvents) { char name[256]; char sBuffer[512]; const char* sampleName = mApplication.mRunning->getName(); SampleUserInput* sampleUserInput = mApplication.getPlatform()->getSampleUserInput(); if(!sampleUserInput) return; strcpy(name,"input"); strcat(name,"/InputEventList.txt"); if(getSampleOutputDirManager().getFilePath(name, sBuffer, false)) { sprintf(name, "input/%s/%sInputEventList.txt", sampleName,mApplication.getPlatform()->getPlatformName()); if(getSampleOutputDirManager().getFilePath(name, sBuffer, false)) { SampleFramework::File* file = NULL; PxToolkit::fopen_s(&file, sBuffer, "w"); if(file) { fputs("InputEventList:\n",file); fputs("----------------------------------------\n",file); for (size_t i = 0; i < inputEvents.size(); i++) { const InputEvent* inputEvent = inputEvents[i]; if(!inputEvent) continue; const std::vector* userInputs = sampleUserInput->getUserInputs(inputEvent->m_Id); const char* name = sampleUserInput->translateInputEventIdToName(inputEvent->m_Id); if(userInputs && !userInputs->empty() && name) { fputs(name,file); fputs("\n",file); } } fclose(file); } } } } void PhysXSample::parseSampleOutputAsset(const char* sampleName,PxU32 userInputCS, PxU32 inputEventCS) { char name[256]; char sBuffer[512]; SampleUserInput* sampleUserInput = mApplication.getPlatform()->getSampleUserInput(); if(!sampleUserInput) return; sprintf(name, "input/%s/%sInputMapping.ods", sampleName,mApplication.getPlatform()->getPlatformName()); if(getSampleOutputDirManager().getFilePath(name, sBuffer, false)) { SampleInputMappingAsset* inputAsset = NULL; SampleFramework::File* fp = NULL; PxToolkit::fopen_s(&fp, sBuffer, "r"); if(fp) { inputAsset = SAMPLE_NEW(SampleInputMappingAsset)(fp,sBuffer, false, userInputCS, inputEventCS); if(!inputAsset->isOk()) { DELETESINGLE(inputAsset); fp = NULL; } } if(inputAsset) { std::vector inputMappings; for (size_t i = inputAsset->getSampleInputData().size();i--;) { const SampleInputData& data = inputAsset->getSampleInputData()[i]; size_t userInputIndex; PxI32 userInputId = sampleUserInput->translateUserInputNameToId(data.m_UserInputName,userInputIndex); size_t inputEventIndex; PxI32 inputEventId = sampleUserInput->translateInputEventNameToId(data.m_InputEventName, inputEventIndex); if(userInputId >= 0 && inputEventId >= 0) { SampleInputMapping mapping; mapping.m_InputEventId = inputEventId; mapping.m_InputEventIndex = inputEventIndex; mapping.m_UserInputId = userInputId; mapping.m_UserInputIndex = userInputIndex; inputMappings.push_back(mapping); } else { //if we get here we read a command mapping from file that is no longer supported in code ... it should be ignored. } } for (size_t i = inputMappings.size(); i--;) { const SampleInputMapping& mapping = inputMappings[i]; sampleUserInput->unregisterInputEvent(mapping.m_InputEventId); } //now I have the default keys definition left, save it to the mapping const std::vector& userInputs = sampleUserInput->getUserInputList(); const std::map >& inputEventUserInputMap = sampleUserInput->getInputEventUserInputMap(); std::map >::const_iterator it = inputEventUserInputMap.begin(); std::map >::const_iterator itEnd = inputEventUserInputMap.end(); while (it != itEnd) { PxU16 inputEventId = it->first; const std::vector& uis = it->second; for (size_t j = 0; j < uis.size(); j++) { const UserInput& ui = userInputs[uis[j]]; const char* eventName = sampleUserInput->translateInputEventIdToName(inputEventId); if(eventName) { inputAsset->addMapping(ui.m_IdName, eventName); } } ++it; } for (size_t i = inputMappings.size(); i--;) { const SampleInputMapping& mapping = inputMappings[i]; sampleUserInput->registerInputEvent(mapping); } } else { // the file does not exist create one SampleFramework::File* fp = NULL; PxToolkit::fopen_s(&fp, sBuffer, "w"); if(fp) { inputAsset = SAMPLE_NEW(SampleInputMappingAsset)(fp,sBuffer,true,userInputCS, inputEventCS); const std::vector& userInputs = sampleUserInput->getUserInputList(); const std::map >& inputEventUserInputMap = sampleUserInput->getInputEventUserInputMap(); std::map >::const_iterator it = inputEventUserInputMap.begin(); std::map >::const_iterator itEnd = inputEventUserInputMap.end(); while (it != itEnd) { PxU16 inputEventId = it->first; const std::vector& uis = it->second; for (size_t j = 0; j < uis.size(); j++) { const UserInput& ui = userInputs[uis[j]]; const char* eventName = sampleUserInput->translateInputEventIdToName(inputEventId); if(eventName) { inputAsset->addMapping(ui.m_IdName, eventName); } } ++it; } } } if(inputAsset) { inputAsset->saveMappings(); DELETESINGLE(inputAsset); } } } void PhysXSample::prepareInputEventUserInputInfo(const char* sampleName,PxU32 &userInputCS, PxU32 &inputEventCS) { SampleUserInput* sampleUserInput = mApplication.getPlatform()->getSampleUserInput(); if(!sampleUserInput) return; saveUserInputs(); std::vector inputEvents; mApplication.collectInputEvents(inputEvents); for (size_t i = inputEvents.size(); i--;) { const InputEvent* ie = inputEvents[i]; inputEventCS += ie->m_Id; const std::vector* userInputs = sampleUserInput->getUserInputs(ie->m_Id); if(userInputs) { for (size_t j = userInputs->size(); j--;) { const UserInput& ui = sampleUserInput->getUserInputList()[ (*userInputs)[j] ]; userInputCS += (ui.m_Id + ie->m_Id); } } } inputEventCS = inputEventCS + ((PxU16)sampleUserInput->getInputEventList().size() << 16); userInputCS = userInputCS + ((PxU16)sampleUserInput->getUserInputList().size() << 16); saveInputEvents(inputEvents); char name[256]; strcpy(name,"input/"); strcat(name,mApplication.getPlatform()->getPlatformName()); strcat(name,"/"); strcat(name,sampleName); strcat(name,"InputMapping.ods"); // load the additional mapping file mSampleInputAsset = (SampleInputAsset*)getAsset(name, SampleAsset::ASSET_INPUT, false); } void PhysXSample::unregisterInputEvents() { mApplication.getPlatform()->getSampleUserInput()->shutdown(); } void PhysXSample::registerInputEvents(bool ignoreSaved) { const char* sampleName = mApplication.mRunning->getName(); SampleUserInput* sampleUserInput = mApplication.getPlatform()->getSampleUserInput(); if(!sampleUserInput) return; PxU32 inputEventCS = 0; PxU32 userInputCS = 0; prepareInputEventUserInputInfo(sampleName, inputEventCS, userInputCS); // register the additional mapping if(mSampleInputAsset) { std::vector inputMappings; for (size_t i = mSampleInputAsset->GetSampleInputData().size();i--;) { const SampleInputData& data = mSampleInputAsset->GetSampleInputData()[i]; size_t userInputIndex; PxI32 userInputId = sampleUserInput->translateUserInputNameToId(data.m_UserInputName,userInputIndex); size_t inputEventIndex; PxI32 inputEventId = sampleUserInput->translateInputEventNameToId(data.m_InputEventName, inputEventIndex); if(userInputId >= 0 && inputEventId >= 0) { SampleInputMapping mapping; mapping.m_InputEventId = inputEventId; mapping.m_InputEventIndex = inputEventIndex; mapping.m_UserInputId = userInputId; mapping.m_UserInputIndex = userInputIndex; inputMappings.push_back(mapping); } else { PX_ASSERT(0); } } for (size_t i = inputMappings.size(); i--;) { const SampleInputMapping& mapping = inputMappings[i]; sampleUserInput->registerInputEvent(mapping); } } if (!ignoreSaved) parseSampleOutputAsset(sampleName, inputEventCS, userInputCS); } void PhysXSample::onKeyDownEx(SampleFramework::SampleUserInput::KeyCode keyCode, PxU32 param) { } void PhysXSample::collectInputEvents(std::vector& inputEvents) { //digital keyboard events DIGITAL_INPUT_EVENT_DEF(SPAWN_DEBUG_OBJECT, WKEY_1, OSXKEY_1, LINUXKEY_1 ); DIGITAL_INPUT_EVENT_DEF(PAUSE_SAMPLE, WKEY_P, OSXKEY_P, LINUXKEY_P ); DIGITAL_INPUT_EVENT_DEF(STEP_ONE_FRAME, WKEY_O, OSXKEY_O, LINUXKEY_O ); DIGITAL_INPUT_EVENT_DEF(TOGGLE_VISUALIZATION, WKEY_V, OSXKEY_V, LINUXKEY_V ); DIGITAL_INPUT_EVENT_DEF(DECREASE_DEBUG_RENDER_SCALE, WKEY_F7, OSXKEY_F7, LINUXKEY_F7 ); DIGITAL_INPUT_EVENT_DEF(INCREASE_DEBUG_RENDER_SCALE, WKEY_F8, OSXKEY_F8, LINUXKEY_F8 ); DIGITAL_INPUT_EVENT_DEF(HIDE_GRAPHICS, WKEY_G, OSXKEY_G, LINUXKEY_G ); DIGITAL_INPUT_EVENT_DEF(WIREFRAME, WKEY_N, OSXKEY_N, LINUXKEY_N ); DIGITAL_INPUT_EVENT_DEF(TOGGLE_PVD_CONNECTION, WKEY_F5, OSXKEY_F5, LINUXKEY_F5 ); DIGITAL_INPUT_EVENT_DEF(SHOW_HELP, WKEY_H, OSXKEY_H, LINUXKEY_H ); DIGITAL_INPUT_EVENT_DEF(SHOW_DESCRIPTION, WKEY_F, OSXKEY_F, LINUXKEY_F ); DIGITAL_INPUT_EVENT_DEF(VARIABLE_TIMESTEP, WKEY_T, OSXKEY_T, LINUXKEY_T ); DIGITAL_INPUT_EVENT_DEF(NEXT_PAGE, WKEY_NEXT, OSXKEY_RIGHT, LINUXKEY_NEXT ); DIGITAL_INPUT_EVENT_DEF(PREVIOUS_PAGE, WKEY_PRIOR, OSXKEY_LEFT, LINUXKEY_PRIOR ); DIGITAL_INPUT_EVENT_DEF(PROFILE_ONLY_PVD, WKEY_9, OSXKEY_UNKNOWN, LINUXKEY_UNKNOWN); //DIGITAL_INPUT_EVENT_DEF(PAUSE_SAMPLE, WKEY_P, OSXKEY_UNKNOWN, LINUXKEY_UNKNOWN); //DIGITAL_INPUT_EVENT_DEF(STEP_ONE_FRAME, WKEY_O, OSXKEY_UNKNOWN, LINUXKEY_UNKNOWN); //digital gamepad events DIGITAL_INPUT_EVENT_DEF(SHOW_HELP, GAMEPAD_SELECT, GAMEPAD_SELECT, LINUXKEY_UNKNOWN); DIGITAL_INPUT_EVENT_DEF(SPAWN_DEBUG_OBJECT, GAMEPAD_NORTH, GAMEPAD_NORTH, LINUXKEY_UNKNOWN); //digital mouse events (are registered in the individual samples as needed) } void PhysXSample::onAnalogInputEvent(const SampleFramework::InputEvent& , float val) { } void PhysXSample::onDigitalInputEvent(const SampleFramework::InputEvent& ie, bool val) { if (!mScene) return; if (val) { if (mShowExtendedHelp) { switch (ie.m_Id) { case NEXT_PAGE: { mExtendedHelpPage++; } break; case PREVIOUS_PAGE: { if(mExtendedHelpPage) mExtendedHelpPage--; } break; } return; } switch (ie.m_Id) { case SPAWN_DEBUG_OBJECT: spawnDebugObject(); break; case PAUSE_SAMPLE: togglePause(); break; case STEP_ONE_FRAME: mOneFrameUpdate = !mOneFrameUpdate; break; case TOGGLE_VISUALIZATION: toggleVisualizationParam(*mScene, PxVisualizationParameter::eSCALE); break; case DECREASE_DEBUG_RENDER_SCALE: { mDebugRenderScale -= 0.1f; mDebugRenderScale = PxMax(mDebugRenderScale, 0.f); mScene->setVisualizationParameter(PxVisualizationParameter::eSCALE, mDebugRenderScale); break; } case INCREASE_DEBUG_RENDER_SCALE: { mDebugRenderScale += 0.1f; mScene->setVisualizationParameter(PxVisualizationParameter::eSCALE, mDebugRenderScale); break; } case HIDE_GRAPHICS: mHideGraphics = !mHideGraphics; break; case WIREFRAME: { mWireFrame = !mWireFrame; break; } case TOGGLE_PVD_CONNECTION: { togglePvdConnection(); break; } case SHOW_HELP: mShowHelp = !mShowHelp; if(mShowHelp) mShowDescription=false; break; case SHOW_DESCRIPTION: mShowDescription = !mShowDescription; if(mShowDescription) mShowHelp=false; break; case VARIABLE_TIMESTEP: mStepperType = (mStepperType == VARIABLE_STEPPER) ? FIXED_STEPPER : VARIABLE_STEPPER; //mUseFixedStepper = !mUseFixedStepper; mFixedStepper.reset(); mVariableStepper.reset(); break; case DELETE_PICKED: if(mPicked && mPicking) { PxActor* pickedActor = mPicking->letGo(); if(pickedActor) pickedActor->release(); } break; case PICKUP: { if(mPicking) { mPicked = true; PxU32 width; PxU32 height; mApplication.getPlatform()->getWindowSize(width, height); mPicking->moveCursor(width/2,height/2); mPicking->lazyPick(); } } break; case PROFILE_ONLY_PVD: if (mPvdParams.useFullPvdConnection) { if(mPvd) { mPvd->disconnect(); mPvdParams.useFullPvdConnection = false; mPvd->connect(*mTransport,physx::PxPvdInstrumentationFlag::ePROFILE); } } break; default: break; } } else { if (mShowExtendedHelp) { return; } if (ie.m_Id == PICKUP) { if(mPicking) { mPicked = false; mPicking->letGo(); } } } } void PhysXSample::toggleFlyCamera() { mIsFlyCamera = !mIsFlyCamera; if (mIsFlyCamera) { mSavedCameraController = getCurrentCameraController(); mApplication.saveCameraState(); mFlyCameraController.init(getCamera().getViewMatrix()); mFlyCameraController.setMouseLookOnMouseButton(false); mFlyCameraController.setMouseSensitivity(0.2f); setCameraController(&mFlyCameraController); } else { mApplication.restoreCameraState(); setCameraController(mSavedCameraController); } } void PhysXSample::togglePause() { //unregisterInputEvents(); mPause = !mPause; if (mEnableAutoFlyCamera) { if (mPause) { mSavedCameraController = getCurrentCameraController(); mApplication.saveCameraState(); mFlyCameraController.init(getCamera().getViewMatrix()); setCameraController(&mFlyCameraController); } else { mApplication.restoreCameraState(); setCameraController(mSavedCameraController); } } //registerInputEvents(true); } void PhysXSample::showExtendedInputEventHelp(PxU32 x, PxU32 y) { const PxReal scale = 0.5f; const PxReal shadowOffset = 6.0f; const RendererColor textColor(255, 255, 255, 255); Renderer* renderer = getRenderer(); const PxU32 yInc = 18; char message[512]; PxU32 width = 0; PxU32 height = 0; renderer->getWindowSize(width, height); y += yInc; PxU16 numIe = (height - y)/yInc - 8; const std::vector inputEventList = getApplication().getPlatform()->getSampleUserInput()->getInputEventList(); const std::vector inputEventNameList = getApplication().getPlatform()->getSampleUserInput()->getInputEventNameList(); size_t maxHelpPage = inputEventList.size()/numIe; if(maxHelpPage < mExtendedHelpPage) mExtendedHelpPage = (PxU8) maxHelpPage; PxU16 printed = 0; PxU16 startPrint = 0; for (size_t i = 0; i < inputEventList.size(); i++) { const InputEvent& ie = inputEventList[i]; const char* msg = mApplication.inputInfoMsg("Press "," to ", ie.m_Id, -1); if(msg) { startPrint++; if(startPrint <= numIe*mExtendedHelpPage) continue; strcpy(message,msg); strcat(message, inputEventNameList[i].m_Name); renderer->print(x, y, message, scale, shadowOffset, textColor); y += yInc; printed++; if(printed >= numIe) { break; } } } if(printed == 0) { if(mExtendedHelpPage) mExtendedHelpPage--; } y += yInc; const char* msg = mApplication.inputInfoMsg("Press "," to show next/previous page", NEXT_PAGE, PREVIOUS_PAGE); if(msg) renderer->print(x, y += yInc, msg, scale, shadowOffset, textColor); } /////////////////////////////////////////////////////////////////////////////// RenderBaseActor* PhysXSample::createRenderBoxFromShape(PxRigidActor* actor, PxShape* shape, RenderMaterial* material, const PxReal* uvs) { RenderBaseActor* shapeRenderActor = NULL; Renderer* renderer = getRenderer(); PX_ASSERT(renderer); PxGeometryType::Enum geomType = shape->getGeometryType(); PX_ASSERT(geomType==PxGeometryType::eBOX); switch(geomType) { case PxGeometryType::eBOX: { // Get physics geometry PxBoxGeometry geometry; bool status = shape->getBoxGeometry(geometry); PX_ASSERT(status); PX_UNUSED(status); // Create render object shapeRenderActor = SAMPLE_NEW(RenderBoxActor)(*renderer, geometry.halfExtents, uvs); } break; default: {} }; if(shapeRenderActor) { link(shapeRenderActor, shape, actor); mRenderActors.push_back(shapeRenderActor); shapeRenderActor->setRenderMaterial(material); shapeRenderActor->setEnableCameraCull(true); } return shapeRenderActor; } RenderBaseActor* PhysXSample::createRenderObjectFromShape(PxRigidActor* actor, PxShape* shape, RenderMaterial* material) { PX_ASSERT(getRenderer()); RenderBaseActor* shapeRenderActor = NULL; Renderer& renderer = *getRenderer(); PxGeometryHolder geom = shape->getGeometry(); switch(geom.getType()) { case PxGeometryType::eSPHERE: shapeRenderActor = SAMPLE_NEW(RenderSphereActor)(renderer, geom.sphere().radius); break; case PxGeometryType::ePLANE: shapeRenderActor = SAMPLE_NEW(RenderGridActor)(renderer, 50, 1.f, PxShapeExt::getGlobalPose(*shape, *actor).q); break; case PxGeometryType::eCAPSULE: shapeRenderActor = SAMPLE_NEW(RenderCapsuleActor)(renderer, geom.capsule().radius, geom.capsule().halfHeight); break; case PxGeometryType::eBOX: shapeRenderActor = SAMPLE_NEW(RenderBoxActor)(renderer, geom.box().halfExtents); break; case PxGeometryType::eCONVEXMESH: { PxConvexMesh* convexMesh = geom.convexMesh().convexMesh; // ### doesn't support scale PxU32 nbVerts = convexMesh->getNbVertices(); PX_UNUSED(nbVerts); const PxVec3* convexVerts = convexMesh->getVertices(); const PxU8* indexBuffer = convexMesh->getIndexBuffer(); PxU32 nbPolygons = convexMesh->getNbPolygons(); PxU32 totalNbTris = 0; PxU32 totalNbVerts = 0; for(PxU32 i=0;igetPolygonData(i, data); PX_ASSERT(status); PX_UNUSED(status); totalNbVerts += data.mNbVerts; totalNbTris += data.mNbVerts - 2; } PxVec3Alloc* allocVerts = SAMPLE_NEW(PxVec3Alloc)[totalNbVerts]; PxVec3Alloc* allocNormals = SAMPLE_NEW(PxVec3Alloc)[totalNbVerts]; PxReal* UVs = (PxReal*)SAMPLE_ALLOC(sizeof(PxReal)*(totalNbVerts * 2)); PxU16* faces = (PxU16*)SAMPLE_ALLOC(sizeof(PxU16)*totalNbTris*3); PxU16* triangles = faces; PxVec3* vertices = allocVerts, * normals = allocNormals; PxU32 offset = 0; for(PxU32 i=0;igetPolygonData(i, face); PX_ASSERT(status); PX_UNUSED(status); const PxU8* faceIndices = indexBuffer+face.mIndexBase; for(PxU32 j=0;jsetMeshScale(geom.convexMesh().scale); SAMPLE_FREE(faces); SAMPLE_FREE(UVs); DELETEARRAY(allocVerts); DELETEARRAY(allocNormals); } break; case PxGeometryType::eTRIANGLEMESH: { // Get physics geometry PxTriangleMesh* tm = geom.triangleMesh().triangleMesh; const PxU32 nbVerts = tm->getNbVertices(); const PxVec3* verts = tm->getVertices(); const PxU32 nbTris = tm->getNbTriangles(); const void* tris = tm->getTriangles(); const bool has16bitIndices = tm->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false; const PxU16* faces16 = has16bitIndices ? (const PxU16*)tris : NULL; const PxU32* faces32 = has16bitIndices ? NULL : (const PxU32*)tris; shapeRenderActor = SAMPLE_NEW(RenderMeshActor)(renderer, verts, nbVerts, NULL, NULL, faces16, faces32, nbTris, true); shapeRenderActor->setMeshScale(geom.triangleMesh().scale); if (!material) material = mManagedMaterials[MATERIAL_FLAT]; } break; case PxGeometryType::eHEIGHTFIELD: { // Get physics geometry const PxHeightFieldGeometry& geometry = geom.heightField(); const PxReal rs = geometry.rowScale; const PxReal hs = geometry.heightScale; const PxReal cs = geometry.columnScale; // Create render object PxHeightField* hf = geometry.heightField; const PxU32 nbCols = hf->getNbColumns(); const PxU32 nbRows = hf->getNbRows(); const PxU32 nbVerts = nbRows * nbCols; const PxU32 nbFaces = (nbCols - 1) * (nbRows - 1) * 2; PxHeightFieldSample* sampleBuffer = new PxHeightFieldSample[nbVerts]; hf->saveCells(sampleBuffer, nbVerts * sizeof(PxHeightFieldSample)); PxVec3* vertexes = new PxVec3[nbVerts]; for(PxU32 i = 0; i < nbRows; i++) { for(PxU32 j = 0; j < nbCols; j++) { vertexes[i * nbCols + j] = PxVec3(PxReal(i) * rs, PxReal(sampleBuffer[j + (i*nbCols)].height) * hs, PxReal(j) * cs); } } PxU32* indices = (PxU32*)SAMPLE_ALLOC(sizeof(PxU32) * nbFaces * 3); for(PxU32 i = 0; i < (nbCols - 1); ++i) { for(PxU32 j = 0; j < (nbRows - 1); ++j) { PxU8 tessFlag = sampleBuffer[i+j*nbCols].tessFlag(); PxU32 i0 = j*nbCols + i; PxU32 i1 = j*nbCols + i + 1; PxU32 i2 = (j+1) * nbCols + i; PxU32 i3 = (j+1) * nbCols + i+ 1; // i2---i3 // | | // | | // i0---i1 // this is really a corner vertex index, not triangle index PxU32 mat0 = hf->getTriangleMaterialIndex((j*nbCols+i)*2); PxU32 mat1 = hf->getTriangleMaterialIndex((j*nbCols+i)*2+1); bool hole0 = (mat0 == PxHeightFieldMaterial::eHOLE); bool hole1 = (mat1 == PxHeightFieldMaterial::eHOLE); // first triangle indices[6 * (i * (nbRows - 1) + j) + 0] = hole0 ? i0 : i2; // duplicate i0 to make a hole indices[6 * (i * (nbRows - 1) + j) + 1] = i0; indices[6 * (i * (nbRows - 1) + j) + 2] = tessFlag ? i3 : i1; // second triangle indices[6 * (i * (nbRows - 1) + j) + 3] = hole1 ? i1 : i3; // duplicate i1 to make a hole indices[6 * (i * (nbRows - 1) + j) + 4] = tessFlag ? i0 : i2; indices[6 * (i * (nbRows - 1) + j) + 5] = i1; } } PxU16* indices_16bit = (PxU16*)SAMPLE_ALLOC(sizeof(PxU16) * nbFaces * 3); for(PxU32 i=0; i< nbFaces; i++) { indices_16bit[i*3+0] = indices[i*3+0]; indices_16bit[i*3+1] = indices[i*3+2]; indices_16bit[i*3+2] = indices[i*3+1]; } shapeRenderActor = SAMPLE_NEW(RenderMeshActor)(renderer, vertexes, nbVerts, NULL, NULL, indices_16bit, NULL, nbFaces); SAMPLE_FREE(indices_16bit); SAMPLE_FREE(indices); DELETEARRAY(sampleBuffer); DELETEARRAY(vertexes); if (!material) material = mManagedMaterials[MATERIAL_FLAT]; } break; default: {} }; if(shapeRenderActor) { link(shapeRenderActor, shape, actor); mRenderActors.push_back(shapeRenderActor); shapeRenderActor->setRenderMaterial(material); shapeRenderActor->setEnableCameraCull(true); } return shapeRenderActor; } void PhysXSample::updateRenderObjectsFromRigidActor(PxRigidActor& actor, RenderMaterial* mat) { PxU32 nbShapes = actor.getNbShapes(); if(nbShapes > 0) { const PxU32 nbShapesOnStack = 8; PxShape* shapesOnStack[nbShapesOnStack], **shapes = &shapesOnStack[0]; if(nbShapes > nbShapesOnStack) shapes = new PxShape*[nbShapes]; actor.getShapes(shapes, nbShapes); for(PxU32 i = 0; i < nbShapes; ++i) { RenderBaseActor* renderActor = getRenderActor(&actor, shapes[i]); if(renderActor != NULL) { renderActor->mActive = true; if (mat != NULL) renderActor->setRenderMaterial(mat); } else createRenderObjectFromShape(&actor, shapes[i], mat); } if(nbShapes > nbShapesOnStack) delete[] shapes; } } void PhysXSample::createRenderObjectsFromScene() { PxScene& scene = getActiveScene(); PxActorTypeFlags types = PxActorTypeFlag::eRIGID_STATIC | PxActorTypeFlag::eRIGID_DYNAMIC; PxU32 nbActors = scene.getNbActors(types); if(nbActors) { PxActor** actors = new PxActor* [nbActors]; scene.getActors(types, actors, nbActors); for(PxU32 i = 0; i < nbActors; ++i) { switch (actors[i]->getType()) { case PxActorType::eRIGID_STATIC: case PxActorType::eRIGID_DYNAMIC: updateRenderObjectsFromRigidActor(*reinterpret_cast(actors[i])); break; default: break; } } delete[] actors; } PxU32 nbArticulations = scene.getNbArticulations(); if(nbArticulations > 0) { PxArticulationBase** articulations = new PxArticulationBase* [nbArticulations]; scene.getArticulations(articulations, nbArticulations); for(PxU32 i=0; i < nbArticulations; i++) { updateRenderObjectsFromArticulation(*articulations[i]); } delete[] articulations; } } void PhysXSample::updateRenderObjectsFromArticulation(PxArticulationBase& articulation) { SampleInlineArray links; links.resize(articulation.getNbLinks()); articulation.getLinks(links.begin(), links.size()); for(PxU32 i=0; i < links.size(); i++) { updateRenderObjectsFromRigidActor(*links[i]); } } void PhysXSample::createRenderObjectsFromActor(PxRigidActor* rigidActor, RenderMaterial* material) { PX_ASSERT(rigidActor); PxU32 nbShapes = rigidActor->getNbShapes(); if(!nbShapes) return; PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*nbShapes); PxU32 nb = rigidActor->getShapes(shapes, nbShapes); PX_ASSERT(nb==nbShapes); PX_UNUSED(nb); for(PxU32 i=0;igetEnableDebugRender()) mRenderActors[i]->drawDebug(debugRenderer); } getCamera().drawDebug(debugRenderer); #ifdef VISUALIZE_PICKING_RAYS if(mPicking) { const std::vector& rays = mPicking->getRays(); PxU32 nbRays = rays.size(); const RendererColor color(255, 0, 0); for(PxU32 i=0;iaddLine(rays[i].origin, rays[i].origin + rays[i].dir * 1000.0f, color); } } #endif #ifdef VISUALIZE_PICKING_TRIANGLES if(mPicking && mPicking->pickedTriangleIsValid()) { PxTriangle touchedTri = mPicking->getPickedTriangle(); RendererColor color(255, 255, 255); debugRenderer->addLine(touchedTri.verts[0], touchedTri.verts[1], color); debugRenderer->addLine(touchedTri.verts[1], touchedTri.verts[2], color); debugRenderer->addLine(touchedTri.verts[2], touchedTri.verts[0], color); for(PxU32 i=0;i<3;i++) { if(mPicking->pickedTriangleAdjacentIsValid(i)) { touchedTri = mPicking->getPickedTriangleAdjacent(i); if(i==0) color = RendererColor(255, 0, 0); else if(i==1) color = RendererColor(0, 255, 0); else if(i==2) color = RendererColor(0, 0, 255); debugRenderer->addLine(touchedTri.verts[0], touchedTri.verts[1], color); debugRenderer->addLine(touchedTri.verts[1], touchedTri.verts[2], color); debugRenderer->addLine(touchedTri.verts[2], touchedTri.verts[0], color); } } } #endif } } void PhysXSample::initRenderObjects() { PxSceneWriteLock scopedLock(*mScene); for (PxU32 i = 0; i < mRenderActors.size(); ++i) { mRenderActors[i]->update(0.0f); } } void PhysXSample::updateRenderObjectsSync(float dtime) { PxSceneWriteLock scopedLock(*mScene); } void PhysXSample::updateRenderObjectsAsync(float dtime) { PX_PROFILE_ZONE("updateRenderObjectsAsync", 0); if(mActiveTransformCount) { PxSceneWriteLock scopedLock(*mScene); PxU32 nbActiveTransforms = mActiveTransformCount; PxActor** currentActor = mBufferedActiveActors; while(nbActiveTransforms--) { PxActor* actor = *currentActor++; // Check that actor is not a deleted actor bool isDeleted = false; for (PxU32 i=0; i < mDeletedActors.size(); i++) { if (mDeletedActors[i] == actor) { isDeleted = true; break; } } if (isDeleted) continue; const PxType actorType = actor->getConcreteType(); if(actorType==PxConcreteType::eRIGID_DYNAMIC || actorType==PxConcreteType::eRIGID_STATIC || actorType == PxConcreteType::eARTICULATION_LINK || actorType == PxConcreteType::eARTICULATION_JOINT) { PxRigidActor* rigidActor = static_cast(actor); PxU32 nbShapes = rigidActor->getNbShapes(); for(PxU32 i=0;igetShapes(&shape, 1, i); PX_ASSERT(n==1); PX_UNUSED(n); RenderBaseActor* renderActor = getRenderActor(rigidActor, shape); if (NULL != renderActor) renderActor->update(dtime); } } } mActiveTransformCount = 0; mDeletedActors.clear(); } } void PhysXSample::bufferActiveTransforms() { PxSceneReadLock scopedLock(*mScene); // buffer active transforms to perform render object update parallel to simulation PxActor** activeTransforms = mScene->getActiveActors(mActiveTransformCount); if(mActiveTransformCapacity < mActiveTransformCount) { SAMPLE_FREE(mBufferedActiveActors); mActiveTransformCapacity = (PxU32)(mActiveTransformCount * 1.5f); mBufferedActiveActors = (PxActor**)SAMPLE_ALLOC(sizeof(PxActor*) * mActiveTransformCapacity); } if(mActiveTransformCount) PxMemCopy(mBufferedActiveActors, activeTransforms, sizeof(PxActor*) * mActiveTransformCount); } /////////////////////////////////////////////////////////////////////////////// RenderMaterial* PhysXSample::createRenderMaterialFromTextureFile(const char* filename) { RenderMaterial* material = NULL; if (!filename) return NULL; RAWTexture data; data.mName = filename; RenderTexture* texture = createRenderTextureFromRawTexture(data); material = SAMPLE_NEW(RenderMaterial)(*getRenderer(), PxVec3(0.7f, 0.7f, 0.7f), 1.0f, true, MATERIAL_CLOTH, texture); mRenderMaterials.push_back(material); return material; } /////////////////////////////////////////////////////////////////////////////// PxRigidActor* PhysXSample::createGrid(RenderMaterial* material) { PxSceneWriteLock scopedLock(*mScene); Renderer* renderer = getRenderer(); PX_ASSERT(renderer); PxRigidStatic* plane = PxCreatePlane(*mPhysics, PxPlane(PxVec3(0,1,0), 0), *mMaterial); if(!plane) fatalError("create plane failed!"); mScene->addActor(*plane); PxShape* shape; plane->getShapes(&shape, 1); RenderGridActor* actor = SAMPLE_NEW(RenderGridActor)(*renderer, 20, 10.0f); link(actor, shape, plane); actor->setTransform(PxTransform(PxIdentity)); mRenderActors.push_back(actor); actor->setRenderMaterial(material); return plane; } /////////////////////////////////////////////////////////////////////////////// void PhysXSample::spawnDebugObject() { PxSceneWriteLock scopedLock(*mScene); PxU32 types = getDebugObjectTypes(); if (!types) return; //select legal type while ((mDebugObjectType & types) == 0) mDebugObjectType = (mDebugObjectType << 1) ? 0 : 1; if ((mDebugObjectType & DEBUG_OBJECT_ALL) == 0) return; const PxVec3 pos = getCamera().getPos(); const PxVec3 vel = getCamera().getViewDir() * getDebugObjectsVelocity(); PxRigidDynamic* actor = NULL; switch (mDebugObjectType) { case DEBUG_OBJECT_SPHERE: actor = createSphere(pos, getDebugSphereObjectRadius(), &vel, mManagedMaterials[MATERIAL_GREEN], mDefaultDensity); break; case DEBUG_OBJECT_BOX: actor = createBox(pos, getDebugBoxObjectExtents(), &vel, mManagedMaterials[MATERIAL_RED], mDefaultDensity); break; case DEBUG_OBJECT_CAPSULE: actor = createCapsule(pos, getDebugCapsuleObjectRadius(), getDebugCapsuleObjectHalfHeight(), &vel, mManagedMaterials[MATERIAL_BLUE], mDefaultDensity); break; case DEBUG_OBJECT_CONVEX: actor = createConvex(pos, &vel, mManagedMaterials[MATERIAL_YELLOW], mDefaultDensity); break; case DEBUG_OBJECT_COMPOUND: actor = createTestCompound(pos, 320, 0.1f, 2.0f, &vel, NULL, mDefaultDensity, true); break; } actor->setWakeCounter(1000000); if (actor) onDebugObjectCreation(actor); //switch type mDebugObjectType = mDebugObjectType << 1; while ((mDebugObjectType & types) == 0) mDebugObjectType = (mDebugObjectType << 1) ? 0 : 1; } /////////////////////////////////////////////////////////////////////////////// PxRigidDynamic* PhysXSample::createBox(const PxVec3& pos, const PxVec3& dims, const PxVec3* linVel, RenderMaterial* material, PxReal density) { PxSceneWriteLock scopedLock(*mScene); PxRigidDynamic* box = PxCreateDynamic(*mPhysics, PxTransform(pos), PxBoxGeometry(dims), *mMaterial, density); PX_ASSERT(box); SetupDefaultRigidDynamic(*box); mScene->addActor(*box); addPhysicsActors(box); if(linVel) box->setLinearVelocity(*linVel); createRenderObjectsFromActor(box, material); return box; } /////////////////////////////////////////////////////////////////////////////// PxRigidDynamic* PhysXSample::createSphere(const PxVec3& pos, PxReal radius, const PxVec3* linVel, RenderMaterial* material, PxReal density) { PxSceneWriteLock scopedLock(*mScene); PxRigidDynamic* sphere = PxCreateDynamic(*mPhysics, PxTransform(pos), PxSphereGeometry(radius), *mMaterial, density); PX_ASSERT(sphere); SetupDefaultRigidDynamic(*sphere); mScene->addActor(*sphere); addPhysicsActors(sphere); if(linVel) sphere->setLinearVelocity(*linVel); createRenderObjectsFromActor(sphere, material); return sphere; } /////////////////////////////////////////////////////////////////////////////// PxRigidDynamic* PhysXSample::createCapsule(const PxVec3& pos, PxReal radius, PxReal halfHeight, const PxVec3* linVel, RenderMaterial* material, PxReal density) { PxSceneWriteLock scopedLock(*mScene); const PxQuat rot = PxQuat(PxIdentity); PX_UNUSED(rot); PxRigidDynamic* capsule = PxCreateDynamic(*mPhysics, PxTransform(pos), PxCapsuleGeometry(radius, halfHeight), *mMaterial, density); PX_ASSERT(capsule); SetupDefaultRigidDynamic(*capsule); mScene->addActor(*capsule); addPhysicsActors(capsule); if(linVel) capsule->setLinearVelocity(*linVel); createRenderObjectsFromActor(capsule, material); return capsule; } /////////////////////////////////////////////////////////////////////////////// PxRigidDynamic* PhysXSample::createConvex(const PxVec3& pos, const PxVec3* linVel, RenderMaterial* material, PxReal density) { PxSceneWriteLock scopedLock(*mScene); PxConvexMesh* convexMesh = GenerateConvex(*mPhysics, *mCooking, getDebugConvexObjectScale(), false, true); PX_ASSERT(convexMesh); PxRigidDynamic* convex = PxCreateDynamic(*mPhysics, PxTransform(pos), PxConvexMeshGeometry(convexMesh), *mMaterial, density); PX_ASSERT(convex); SetupDefaultRigidDynamic(*convex); mScene->addActor(*convex); addPhysicsActors(convex); if(linVel) convex->setLinearVelocity(*linVel); createRenderObjectsFromActor(convex, material); return convex; } /////////////////////////////////////////////////////////////////////////////// PxRigidDynamic* PhysXSample::createCompound(const PxVec3& pos, const std::vector& localPoses, const std::vector& geometries, const PxVec3* linVel, RenderMaterial* material, PxReal density) { PxSceneWriteLock scopedLock(*mScene); PxRigidDynamic* compound = GenerateCompound(*mPhysics, mScene, mMaterial, pos, PxQuat(PxIdentity), localPoses, geometries, false, density); addPhysicsActors(compound); if(linVel) compound->setLinearVelocity(*linVel); createRenderObjectsFromActor(compound, material); return compound; } PxRigidDynamic* PhysXSample::createTestCompound(const PxVec3& pos, PxU32 nbBoxes, float boxSize, float amplitude, const PxVec3* vel, RenderMaterial* material, PxReal density, bool makeSureVolumeEmpty) { PxSceneWriteLock scopedLock(*mScene); if (makeSureVolumeEmpty) { // Kai: a little bigger than amplitude + boxSize * sqrt(3) PxSphereGeometry geometry(amplitude + boxSize + boxSize); PxTransform pose(pos); PxOverlapBuffer buf; getActiveScene().overlap(geometry, pose, buf, PxQueryFilterData(PxQueryFlag::eANY_HIT|PxQueryFlag::eSTATIC|PxQueryFlag::eDYNAMIC)); if (buf.hasBlock) { // shdfnd::printFormatted("desination volume is not empty!!!\n"); return NULL; } } std::vector localPoses; std::vector geometries; PxToolkit::BasicRandom rnd(42); PxBoxGeometryAlloc* geoms = SAMPLE_NEW(PxBoxGeometryAlloc)[nbBoxes]; for(PxU32 i=0;igetPhysicsShape() == mPxShape; } PxShape* mPxShape; }; void PhysXSample::removeRenderObject(RenderBaseActor* renderActor) { std::vector::iterator renderIter = std::find(mRenderActors.begin(), mRenderActors.end(), renderActor); if(renderIter != mRenderActors.end()) { // ######### PT: TODO: unlink call missing here!!! mDeletedRenderActors.push_back((*renderIter)); mRenderActors.erase(renderIter); } } ////////////////////////////////////////////////////////////////////////// void PhysXSample::removeRenderActorsFromPhysicsActor(const PxRigidActor* actor) { 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]; FindRenderActor findRenderActor(shape); std::vector::iterator renderIter = std::find_if(mRenderActors.begin(), mRenderActors.end(), findRenderActor); if(renderIter != mRenderActors.end()) { unlink((*renderIter), shape, const_cast(actor)); mDeletedRenderActors.push_back((*renderIter)); mRenderActors.erase(renderIter); } } // check if the actor is in the active transform list and remove if(actor->getType() == PxActorType::eRIGID_DYNAMIC) { for(PxU32 i=0; i < mActiveTransformCount; i++) { if(mBufferedActiveActors[i] == actor) { mBufferedActiveActors[i] = mBufferedActiveActors[mActiveTransformCount-1]; mActiveTransformCount--; break; } } mDeletedActors.push_back(const_cast(actor)); } SAMPLE_FREE(shapes); } ////////////////////////////////////////////////////////////////////////// void PhysXSample::removeActor(PxRigidActor* actor) { removeRenderActorsFromPhysicsActor(actor); std::vector::iterator actorIter = std::find(mPhysicsActors.begin(), mPhysicsActors.end(), actor); if(actorIter != mPhysicsActors.end()) { mPhysicsActors.erase(actorIter); actor->release(); } } /////////////////////////////////////////////////////////////////////////////// SampleFramework::SampleAsset* PhysXSample::getAsset(const char* relativePath, SampleFramework::SampleAsset::Type type, bool abortOnError) { SampleFramework::SampleAsset* asset = mApplication.getAssetManager()->getAsset(relativePath, type); if (NULL == asset && abortOnError) { std::string msg = "Error while getting material asset "; msg += relativePath; msg += "\n"; fatalError(msg.c_str()); } return asset; } void PhysXSample::importRAWFile(const char* relativePath, PxReal scale, bool recook) { mMeshTag = 0; mFilename = relativePath; mScale = scale; const bool saved = gRecook; if(!gRecook && recook) gRecook = true; bool status = loadRAWfile(getSampleMediaFilename(mFilename), *this, scale); if (!status) { std::string msg = "Sample can not load file "; msg += getSampleMediaFilename(mFilename); msg += "\n"; fatalError(msg.c_str()); } gRecook = saved; }