diff options
Diffstat (limited to 'PhysX_3.4/Samples/SampleBase/PhysXSample.cpp')
| -rw-r--r-- | PhysX_3.4/Samples/SampleBase/PhysXSample.cpp | 2901 |
1 files changed, 2901 insertions, 0 deletions
diff --git a/PhysX_3.4/Samples/SampleBase/PhysXSample.cpp b/PhysX_3.4/Samples/SampleBase/PhysXSample.cpp new file mode 100644 index 00000000..f72e789b --- /dev/null +++ b/PhysX_3.4/Samples/SampleBase/PhysXSample.cpp @@ -0,0 +1,2901 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +// proprietary rights in and to this software and related documentation and +// any modifications thereto. Any use, reproduction, disclosure, or +// distribution of this software and related documentation without an express +// license agreement from NVIDIA Corporation is strictly prohibited. +// +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright (c) 2008-2016 NVIDIA Corporation. All rights reserved. + +#include "foundation/PxFoundationVersion.h" + +#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 "RenderClothActor.h" +#include "RendererClothShape.h" +#include "ParticleSystem.h" + +#include <SamplePlatform.h> +#include "SampleBaseInputEventIds.h" +#include <SampleUserInputIds.h> +#include "SampleUserInputDefines.h" +#include <SampleInputAsset.h> +#include "SampleInputMappingAsset.h" + +#include <algorithm> +#include <ctype.h> + +#include "cloth/PxClothParticleData.h" +#include "TestClothHelpers.h" +#include "extensions/PxClothFabricCooker.h" + +#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, +#ifdef RENDERER_TABLET + MATERIAL_CONTROLS, + MATERIAL_BUTTONS, +#endif +}; +#ifdef RENDERER_TABLET +#include "SampleMaterialAsset.h" +static const char* controlMaterialPath = "materials/tablet_sticks.xml"; +static const char* buttonMaterialPath = "materials/tablet_buttons.xml"; +#endif + +/////////////////////////////////////////////////////////////////////////////// + +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_DIR, 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<nb;i++) + { + const PxF32 angle = 6.28f*PxF32(i)/PxF32(nb); + pts[i].x = cosf(angle)*scale; + pts[i].y = z; + pts[i].z = sinf(angle)*scale; + } +} + +static PxConvexMesh* GenerateConvex(PxPhysics& sdk, PxCooking& cooking, PxU32 nbVerts, const PxVec3* verts, bool recenterVerts=false) +{ + PxVec3Alloc* tmp = NULL; + if(recenterVerts) + { + PxVec3 center(0); + for(PxU32 i=0;i<nbVerts;i++) + center += verts[i]; + center /= PxReal(nbVerts); + + tmp = SAMPLE_NEW(PxVec3Alloc)[nbVerts]; + PxVec3* recentered = tmp; + for(PxU32 i=0;i<nbVerts;i++) + recentered[i] = verts[i] - center; + } + + PxConvexMesh* convexMesh = PxToolkit::createConvexMesh(sdk, cooking, recenterVerts ? tmp : verts, nbVerts, PxConvexFlag::eCOMPUTE_CONVEX); + + DELETEARRAY(tmp); + + return convexMesh; +} + +static PxConvexMesh* GenerateConvex(PxPhysics& sdk, PxCooking& cooking, float scale, bool large=false, bool randomize=true) +{ + const PxI32 minNb = large ? 16 : 3; + const PxI32 maxNb = large ? 32 : 8; + const int nbInsideCirclePts = !randomize ? 3 : getSampleRandom().rand(minNb, maxNb); + const int nbOutsideCirclePts = !randomize ? 8 : getSampleRandom().rand(minNb, maxNb); + const int nbVerts = nbInsideCirclePts + nbOutsideCirclePts; + + // Generate random vertices + PxVec3Alloc* verts = SAMPLE_NEW(PxVec3Alloc)[nbVerts]; + + // Two methods + if(randomize && getSampleRandom().rand(0, 100) > 50) + { + for(int i=0;i<nbVerts;i++) + { + verts[i].x = scale * getSampleRandom().rand(-2.5f, 2.5f); + verts[i].y = scale * getSampleRandom().rand(-2.5f, 2.5f); + verts[i].z = scale * getSampleRandom().rand(-2.5f, 2.5f); + } + } + else + { + GenerateCirclePts(nbInsideCirclePts, verts, scale, 0.0f); + GenerateCirclePts(nbOutsideCirclePts, verts+nbInsideCirclePts, scale*3.0f, 10.0f*scale); + } + + PxConvexMesh* convexMesh = GenerateConvex(sdk, cooking, nbVerts, verts); + + DELETEARRAY(verts); + return convexMesh; +} + +#if 0 + +static PxConvexMesh* GenerateConvex(PxPhysics& sdk, PxCooking& cooking, int nbInsideCirclePts, int nbOutsideCirclePts, float scale0, float scale1, float z) +{ + const int nbVerts = nbInsideCirclePts + nbOutsideCirclePts; + + // Generate random vertices + PxVec3Alloc* verts = SAMPLE_NEW(PxVec3Alloc)[nbVerts]; + + GenerateCirclePts(nbInsideCirclePts, verts, scale0, 0.0f); + GenerateCirclePts(nbOutsideCirclePts, verts+nbInsideCirclePts, scale1, z); + + PxConvexMesh* convexMesh = GenerateConvex(sdk, cooking, nbVerts, verts); + + DELETEARRAY(verts); + return convexMesh; +} + +#endif + +static PxRigidDynamic* GenerateCompound(PxPhysics& sdk, PxScene* scene, PxMaterial* defaultMat, const PxVec3& pos, const PxQuat& rot, const std::vector<PxTransform>& poses, const std::vector<const PxGeometry*>& 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;i<poses.size();i++) + { + const PxTransform& currentPose = poses[i]; + const PxGeometry* currentGeom = geometries[i]; + + PxShape* shape = PxRigidActorExt::createExclusiveShape(*actor, *currentGeom, *defaultMat); + shape->setLocalPose(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<PxRigidActor>()) + { + const PxRigidActor* actor = static_cast<const PxRigidActor*>(observed); + + removeRenderActorsFromPhysicsActor(actor); + + std::vector<PxRigidActor*>::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;i<nbTris;i++) + { + indices[i*3+0] = src[i*3+0]; + indices[i*3+1] = src[i*3+2]; + indices[i*3+2] = src[i*3+1]; + } + + RenderMeshActor* meshActor = SAMPLE_NEW(RenderMeshActor)(*getRenderer(), data.mVerts, data.mNbVerts, data.mVertexNormals, data.mUVs, indices, NULL, nbTris); + SAMPLE_FREE(indices); + + if(data.mMaterialID!=0xffffffff) + { + size_t nbMaterials = mRenderMaterials.size(); + for(PxU32 i=0;i<nbMaterials;i++) + { + if(mRenderMaterials[i]->mID==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<SampleTextureAsset*>(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;i<nbTextures;i++) + { + if(mRenderTextures[i]->mID==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;i<nbTris;i++) + { + indices[i*3+0] = src[i*3+0]; + indices[i*3+1] = src[i*3+2]; + indices[i*3+2] = src[i*3+1]; + } + + RenderMeshActor* meshActor = SAMPLE_NEW(RenderMeshActor)(*getRenderer(), verts, nbVerts, verts, NULL, indices, NULL, nbTris); + if(data.mName) + strcpy(meshActor->mName, 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(!mBufferedActiveTransforms); + 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), + mBufferedActiveTransforms(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); + +#if defined(RENDERER_TABLET) + getRenderer()->releaseAllButtons(); +#endif + + releaseAll(mRenderActors); + releaseAll(mRenderTextures); + releaseAll(mRenderMaterials); + { + PxSceneWriteLock scopedLock(*mScene); + releaseAll(mPhysicsActors); + } + + SAMPLE_FREE(mBufferedActiveTransforms); + mFixedStepper.shutdown(); + mDebugStepper.shutdown(); + mVariableStepper.shutdown(); + + const size_t nbManagedAssets = mManagedAssets.size(); + if(nbManagedAssets) + { + SampleAssetManager* assetManager = mApplication.getAssetManager(); + for(PxU32 i=0; i<nbManagedAssets; i++) + assetManager->returnAsset(*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;i<nbRegions;i++) + { + PxBroadPhaseRegion region; + region.bounds = bounds[i]; + region.userData = (void*)i; + scene.addBroadPhaseRegion(region); + } +} +#endif + + + +void PhysXSample::onInit() +{ + + //Recording memory allocations is necessary if you want to + //use the memory facilities in PVD effectively. Since PVD isn't necessarily connected + //right away, we add a mechanism that records all outstanding memory allocations and + //forwards them to PVD when it does connect. + + //This certainly has a performance and memory profile effect and thus should be used + //only in non-production builds. + bool recordMemoryAllocations = true; +#ifdef RENDERER_ANDROID + const bool useCustomTrackingAllocator = false; +#else + const bool useCustomTrackingAllocator = true; +#endif + + PxAllocatorCallback* allocator = &gDefaultAllocatorCallback; + + if(useCustomTrackingAllocator) + allocator = getSampleAllocator(); //optional override that will track memory allocations + + mFoundation = PxCreateFoundation(PX_FOUNDATION_VERSION, *allocator, getSampleErrorCallback()); + if(!mFoundation) + fatalError("PxCreateFoundation failed!"); + +#if PX_SUPPORT_GPU_PHYSX + if(mCreateCudaCtxManager) + { + PxCudaContextManagerDesc cudaContextManagerDesc; + +#if defined(RENDERER_ENABLE_CUDA_INTEROP) + if (!mApplication.getCommandLine().hasSwitch("nointerop")) + { + switch(getRenderer()->getDriverType()) + { + 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); + 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!"); + +#if defined(RENDERER_TABLET) + // load touchscreen control material + { + SampleFramework::SampleAsset* ps_asset = getAsset(controlMaterialPath, SampleFramework::SampleAsset::ASSET_MATERIAL); + mManagedAssets.push_back(ps_asset); + PX_ASSERT(ps_asset->getType() == SampleFramework::SampleAsset::ASSET_MATERIAL); + SampleFramework::SampleMaterialAsset* mat_ps_asset = static_cast<SampleFramework::SampleMaterialAsset*>(ps_asset); + if(mat_ps_asset->getNumVertexShaders() > 0) + { + RenderMaterial* mat = SAMPLE_NEW(RenderMaterial)(*getRenderer(), mat_ps_asset->getMaterial(0), mat_ps_asset->getMaterialInstance(0), MATERIAL_CONTROLS); + mRenderMaterials.push_back(mat); + } + } + // load touchscreen button material + { + SampleFramework::SampleAsset* ps_asset = getAsset(buttonMaterialPath, SampleFramework::SampleAsset::ASSET_MATERIAL); + mManagedAssets.push_back(ps_asset); + PX_ASSERT(ps_asset->getType() == SampleFramework::SampleAsset::ASSET_MATERIAL); + SampleFramework::SampleMaterialAsset* mat_ps_asset = static_cast<SampleFramework::SampleMaterialAsset*>(ps_asset); + if(mat_ps_asset->getNumVertexShaders() > 0) + { + RenderMaterial* mat = SAMPLE_NEW(RenderMaterial)(*getRenderer(), mat_ps_asset->getMaterial(0), mat_ps_asset->getMaterialInstance(0), MATERIAL_BUTTONS); + mRenderMaterials.push_back(mat); + } + } + Renderer* renderer = getRenderer(); + RenderMaterial* controlMaterial = getMaterial(MATERIAL_CONTROLS); + renderer->initControls(controlMaterial->mRenderMaterial, + controlMaterial->mRenderMaterialInstance); + RenderMaterial* buttonMaterial = getMaterial(MATERIAL_BUTTONS); + // add buttons for common use + PxReal yInc = -0.12f; + PxVec2 leftBottom(0.58f, 0.90f); + PxVec2 rightTop(0.99f, 0.82f); + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + + // add buttons for individual sample + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + + // quick access button + leftBottom.y += yInc; rightTop.y += yInc; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + + // next, previous buttons + leftBottom.x = -0.4f; + leftBottom.y = 0.9f; + rightTop.x = -0.1f; + rightTop.y = 0.82f; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + leftBottom.x = 0.1f; + leftBottom.y = 0.9f; + rightTop.x = 0.4f; + rightTop.y = 0.82f; + renderer->addButton(leftBottom, rightTop, NULL, + buttonMaterial->mRenderMaterial, buttonMaterial->mRenderMaterialInstance); + +#endif + + 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.gpuDispatcher && mCudaContextManager) + sceneDesc.gpuDispatcher = mCudaContextManager->getGpuDispatcher(); +#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_ACTIVETRANSFORMS; + sceneDesc.flags |= PxSceneFlag::eSUPPRESS_EAGER_SCENE_QUERY_REFIT; + //sceneDesc.flags |= PxSceneFlag::eDISABLE_CONTACT_CACHE; + sceneDesc.broadPhaseType = PxBroadPhaseType::eGPU; + sceneDesc.gpuMaxNumPartitions = 8; + + +#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<UserInput>& 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<const InputEvent*>& 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<size_t>* 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<SampleInputMapping> 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<UserInput>& userInputs = sampleUserInput->getUserInputList(); + const std::map<physx::PxU16, std::vector<size_t> >& inputEventUserInputMap = sampleUserInput->getInputEventUserInputMap(); + std::map<physx::PxU16, std::vector<size_t> >::const_iterator it = inputEventUserInputMap.begin(); + std::map<physx::PxU16, std::vector<size_t> >::const_iterator itEnd = inputEventUserInputMap.end(); + while (it != itEnd) + { + PxU16 inputEventId = it->first; + const std::vector<size_t>& 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<UserInput>& userInputs = sampleUserInput->getUserInputList(); + const std::map<physx::PxU16, std::vector<size_t> >& inputEventUserInputMap = sampleUserInput->getInputEventUserInputMap(); + std::map<physx::PxU16, std::vector<size_t> >::const_iterator it = inputEventUserInputMap.begin(); + std::map<physx::PxU16, std::vector<size_t> >::const_iterator itEnd = inputEventUserInputMap.end(); + while (it != itEnd) + { + PxU16 inputEventId = it->first; + const std::vector<size_t>& 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<const InputEvent*> inputEvents; + mApplication.collectInputEvents(inputEvents); + + for (size_t i = inputEvents.size(); i--;) + { + const InputEvent* ie = inputEvents[i]; + inputEventCS += ie->m_Id; + + const std::vector<size_t>* 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<SampleInputMapping> 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 !defined(RENDERER_TABLET) + if (!ignoreSaved) + parseSampleOutputAsset(sampleName, inputEventCS, userInputCS); +#endif +} + +void PhysXSample::onKeyDownEx(SampleFramework::SampleUserInput::KeyCode keyCode, PxU32 param) +{ +} + +void PhysXSample::collectInputEvents(std::vector<const SampleFramework::InputEvent*>& inputEvents) +{ + //digital keyboard events + DIGITAL_INPUT_EVENT_DEF(SPAWN_DEBUG_OBJECT, WKEY_1, XKEY_1, X1KEY_1, PS3KEY_1, PS4KEY_1, AKEY_UNKNOWN, OSXKEY_1, IKEY_UNKNOWN, LINUXKEY_1, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(PAUSE_SAMPLE, WKEY_P, XKEY_P, X1KEY_P, PS3KEY_P, PS4KEY_P, AKEY_UNKNOWN, OSXKEY_P, IKEY_UNKNOWN, LINUXKEY_P, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(STEP_ONE_FRAME, WKEY_O, XKEY_O, X1KEY_O, PS3KEY_O, PS4KEY_O, AKEY_UNKNOWN, OSXKEY_O, IKEY_UNKNOWN, LINUXKEY_O, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(TOGGLE_VISUALIZATION, WKEY_V, XKEY_V, X1KEY_V, PS3KEY_V, PS4KEY_V, AKEY_UNKNOWN, OSXKEY_V, IKEY_UNKNOWN, LINUXKEY_V, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(DECREASE_DEBUG_RENDER_SCALE, WKEY_F7, XKEY_F7, X1KEY_F7, PS3KEY_F7, PS4KEY_F7, AKEY_UNKNOWN, OSXKEY_F7, IKEY_UNKNOWN, LINUXKEY_F7, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(INCREASE_DEBUG_RENDER_SCALE, WKEY_F8, XKEY_F8, X1KEY_F8, PS3KEY_F8, PS4KEY_F8, AKEY_UNKNOWN, OSXKEY_F8, IKEY_UNKNOWN, LINUXKEY_F8, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(HIDE_GRAPHICS, WKEY_G, XKEY_G, X1KEY_G, PS3KEY_G, PS4KEY_G, AKEY_UNKNOWN, OSXKEY_G, IKEY_UNKNOWN, LINUXKEY_G, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(WIREFRAME, WKEY_N, XKEY_N, X1KEY_N, PS3KEY_N, PS4KEY_N, AKEY_UNKNOWN, OSXKEY_N, IKEY_UNKNOWN, LINUXKEY_N, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(TOGGLE_PVD_CONNECTION, WKEY_F5, XKEY_F5, X1KEY_F5, PS3KEY_F5, PS4KEY_F5, AKEY_UNKNOWN, OSXKEY_F5, IKEY_UNKNOWN, LINUXKEY_F5, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(SHOW_HELP, WKEY_H, XKEY_H, X1KEY_H, PS3KEY_H, PS4KEY_H, AKEY_UNKNOWN, OSXKEY_H, IKEY_UNKNOWN, LINUXKEY_H, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(SHOW_DESCRIPTION, WKEY_F, XKEY_F, X1KEY_F, PS3KEY_F, PS4KEY_F, AKEY_UNKNOWN, OSXKEY_F, IKEY_UNKNOWN, LINUXKEY_F, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(VARIABLE_TIMESTEP, WKEY_T, XKEY_T, X1KEY_T, PS3KEY_T, PS4KEY_T, AKEY_UNKNOWN, OSXKEY_T, IKEY_UNKNOWN, LINUXKEY_T, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(NEXT_PAGE, WKEY_NEXT, XKEY_UNKNOWN, X1KEY_UNKNOWN, PS3KEY_NEXT, PS4KEY_NEXT, AKEY_UNKNOWN, OSXKEY_RIGHT, IKEY_UNKNOWN, LINUXKEY_NEXT, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(PREVIOUS_PAGE, WKEY_PRIOR, XKEY_UNKNOWN, X1KEY_UNKNOWN, PS3KEY_PRIOR, PS4KEY_PRIOR, AKEY_UNKNOWN, OSXKEY_LEFT, IKEY_UNKNOWN, LINUXKEY_PRIOR, WIIUKEY_UNKNOWN ); + DIGITAL_INPUT_EVENT_DEF(PROFILE_ONLY_PVD, WKEY_9, XKEY_UNKNOWN, X1KEY_UNKNOWN, PS3KEY_UNKNOWN, PS4KEY_UNKNOWN, AKEY_UNKNOWN, OSXKEY_UNKNOWN, IKEY_UNKNOWN, LINUXKEY_UNKNOWN, WIIUKEY_UNKNOWN ); + //DIGITAL_INPUT_EVENT_DEF(PAUSE_SAMPLE, WKEY_P, XKEY_UNKNOWN, X1KEY_UNKNOWN, PS3KEY_UNKNOWN, PS4KEY_UNKNOWN, AKEY_UNKNOWN, OSXKEY_UNKNOWN, IKEY_UNKNOWN, LINUXKEY_UNKNOWN); + //DIGITAL_INPUT_EVENT_DEF(STEP_ONE_FRAME, WKEY_O, XKEY_UNKNOWN, X1KEY_UNKNOWN, PS3KEY_UNKNOWN, PS4KEY_UNKNOWN, AKEY_UNKNOWN, OSXKEY_UNKNOWN, IKEY_UNKNOWN, LINUXKEY_UNKNOWN); + + //digital gamepad events + DIGITAL_INPUT_EVENT_DEF(SHOW_HELP, GAMEPAD_SELECT, GAMEPAD_SELECT, GAMEPAD_SELECT, GAMEPAD_SELECT, GAMEPAD_SELECT, AKEY_UNKNOWN, GAMEPAD_SELECT, IKEY_UNKNOWN, LINUXKEY_UNKNOWN, GAMEPAD_SELECT); + DIGITAL_INPUT_EVENT_DEF(SPAWN_DEBUG_OBJECT, GAMEPAD_NORTH, GAMEPAD_NORTH, GAMEPAD_NORTH, GAMEPAD_NORTH, GAMEPAD_NORTH, AKEY_UNKNOWN, GAMEPAD_NORTH, IKEY_UNKNOWN, LINUXKEY_UNKNOWN, GAMEPAD_NORTH); + + //digital mouse events (are registered in the individual samples as needed) + + //touch events (reserve first 4 buttons for common use, individual samples start from 5) + TOUCH_INPUT_EVENT_DEF(PAUSE_SAMPLE, "Pause", ABUTTON_1, IBUTTON_1); + TOUCH_INPUT_EVENT_DEF(STEP_ONE_FRAME, "Single Step", ABUTTON_2, IBUTTON_2); +} + +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<InputEvent> inputEventList = getApplication().getPlatform()->getSampleUserInput()->getInputEventList(); + const std::vector<InputEventName> 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;i<nbPolygons;i++) + { + PxHullPolygon data; + bool status = convexMesh->getPolygonData(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;i<nbPolygons;i++) + { + PxHullPolygon face; + bool status = convexMesh->getPolygonData(i, face); + PX_ASSERT(status); + PX_UNUSED(status); + + const PxU8* faceIndices = indexBuffer+face.mIndexBase; + for(PxU32 j=0;j<face.mNbVerts;j++) + { + vertices[offset+j] = convexVerts[faceIndices[j]]; + normals[offset+j] = PxVec3(face.mPlane[0], face.mPlane[1], face.mPlane[2]); + } + + for(PxU32 j=2;j<face.mNbVerts;j++) + { + *triangles++ = PxU16(offset); + *triangles++ = PxU16(offset+j); + *triangles++ = PxU16(offset+j-1); + } + + offset += face.mNbVerts; + } + + // prepare UVs for convex: + // filling like this + // vertice #0 - 0,0 + // vertice #1 - 0,1 + // vertice #2 - 1,0 + // vertice #3 - 0,0 + // ... + for(PxU32 i = 0; i < totalNbVerts; ++i) + { + PxU32 c = i % 3; + if(c == 0) + { + UVs[2 * i] = 0.0f; + UVs[2 * i + 1] = 0.0f; + } + else if(c == 1) + { + UVs[2 * i] = 0.0f; + UVs[2 * i + 1] = 1.0f; + } + else if(c == 2) + { + UVs[2 * i] = 1.0f; + UVs[2 * i + 1] = 0.0f; + } + } + + PX_ASSERT(offset==totalNbVerts); + shapeRenderActor = SAMPLE_NEW(RenderMeshActor)(renderer, vertices, totalNbVerts, normals, UVs, faces, NULL, totalNbTris); + shapeRenderActor->setMeshScale(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; + +#if PX_USE_PARTICLE_SYSTEM_API + types |= PxActorTypeFlag::ePARTICLE_SYSTEM | PxActorTypeFlag::ePARTICLE_FLUID; +#endif + +#if PX_USE_CLOTH_API + types |= PxActorTypeFlag::eCLOTH; +#endif + + 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<PxRigidActor*>(actors[i])); + break; +#if PX_USE_PARTICLE_SYSTEM_API + case PxActorType::ePARTICLE_SYSTEM: + case PxActorType::ePARTICLE_FLUID: + // updateRenderObjectsFromRigidActor(*reinterpret_cast<PxRigidActor*>(actors[i])); + // break; +#endif +#if PX_USE_CLOTH_API + case PxActorType::eCLOTH: +#endif + default: + break; + } + } + delete[] actors; + } + + PxU32 nbArticulations = scene.getNbArticulations(); + if(nbArticulations > 0) + { + PxArticulation** articulations = new PxArticulation* [nbArticulations]; + scene.getArticulations(articulations, nbArticulations); + for(PxU32 i=0; i < nbArticulations; i++) + { + updateRenderObjectsFromArticulation(*articulations[i]); + } + delete[] articulations; + } +} + +void PhysXSample::updateRenderObjectsFromArticulation(PxArticulation& articulation) +{ + SampleInlineArray<PxArticulationLink*,20> 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;i<nbShapes;i++) + { + createRenderObjectFromShape(rigidActor, shapes[i], material); + } + SAMPLE_FREE(shapes); +} + +void PhysXSample::updateRenderObjectsDebug(float dtime) +{ + RenderPhysX3Debug* debugRenderer = getDebugRenderer(); + if(debugRenderer && mScene) + { + for(PxU32 i = 0; i < mRenderActors.size(); ++i) + { + if (mRenderActors[i]->getEnableDebugRender()) + mRenderActors[i]->drawDebug(debugRenderer); + } + getCamera().drawDebug(debugRenderer); + +#ifdef VISUALIZE_PICKING_RAYS + if(mPicking) + { + const std::vector<Picking::Ray>& rays = mPicking->getRays(); + PxU32 nbRays = rays.size(); + const RendererColor color(255, 0, 0); + for(PxU32 i=0;i<nbRays;i++) + { + debugRenderer->addLine(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); +#if PX_USE_CLOTH_API + +#if defined(RENDERER_ENABLE_CUDA_INTEROP) + + // map interop resources + shdfnd::Array<RendererClothShape*> shapes; + + if (mCudaContextManager) + { + for(PxU32 i = 0; i < mRenderClothActors.size(); ++i) + { + if (mRenderClothActors[i]->getCloth()->getClothFlags()&PxClothFlag::eGPU && + mRenderClothActors[i]->getRenderClothShape()->isInteropEnabled()) + { + shapes.pushBack(mRenderClothActors[i]->getRenderClothShape()); + } + } + + if (shapes.size()) + { + mCudaContextManager->acquireContext(); + RendererClothShape::mapShapes(&shapes[0], shapes.size()); + } + } +#endif // RENDERER_ENABLE_CUDA_INTEROP + + // update shapes + for(PxU32 i = 0; i < mRenderClothActors.size(); ++i) + mRenderClothActors[i]->update(dtime); + +#if defined(RENDERER_ENABLE_CUDA_INTEROP) + + // unmap interop resources + if (mCudaContextManager && shapes.size()) + { + RendererClothShape::unmapShapes(&shapes[0], shapes.size()); + mCudaContextManager->releaseContext(); + } + +#endif // RENDERER_ENABLE_CUDA_INTEROP + +#endif // PX_USE_CLOTH_API + +#if PX_USE_PARTICLE_SYSTEM_API + for(PxU32 i = 0; i < mRenderParticleSystemActors.size(); ++i) + mRenderParticleSystemActors[i]->update(dtime); +#endif // PX_USE_PARTICLE_SYSTEM_API +} + +void PhysXSample::updateRenderObjectsAsync(float dtime) +{ + PX_PROFILE_ZONE("updateRenderObjectsAsync", 0); + if(mActiveTransformCount) + { + PxSceneWriteLock scopedLock(*mScene); + PxU32 nbActiveTransforms = mActiveTransformCount; + PxActiveTransform* currentTransform = mBufferedActiveTransforms; + while(nbActiveTransforms--) + { + const PxActiveTransform& activeTransform = *currentTransform++; + PxActor* actor = activeTransform.actor; + + // 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<PxRigidActor*>(actor); + PxU32 nbShapes = rigidActor->getNbShapes(); + for(PxU32 i=0;i<nbShapes;i++) + { + PxShape* shape; + PxU32 n = rigidActor->getShapes(&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 + + const PxActiveTransform* activeTransforms = mScene->getActiveTransforms(mActiveTransformCount); + if(mActiveTransformCapacity < mActiveTransformCount) + { + SAMPLE_FREE(mBufferedActiveTransforms); + mActiveTransformCapacity = (PxU32)(mActiveTransformCount * 1.5f); + mBufferedActiveTransforms = (PxActiveTransform*)SAMPLE_ALLOC(sizeof(PxActiveTransform) * mActiveTransformCapacity); + } + if(mActiveTransformCount) + PxMemCopy(mBufferedActiveTransforms, activeTransforms, sizeof(PxActiveTransform) * mActiveTransformCount); + +} + +/////////////////////////////////////////////////////////////////////////////// +#if PX_USE_CLOTH_API + +PxCloth* PhysXSample::createClothFromMeshDesc( + const PxClothMeshDesc& meshDesc, const PxTransform& pose, const PxVec3& gravityDir, const PxVec2* uv, const char* textureFile, PxReal scale) +{ + PxClothFabric* clothFabric = PxClothFabricCreate(getPhysics(), meshDesc, gravityDir); + PX_ASSERT(meshDesc.points.stride == sizeof(PxVec4)); + + // create the cloth actor + const PxClothParticle* particles = (const PxClothParticle*)meshDesc.points.data; + PxCloth* cloth = getPhysics().createCloth( pose, *clothFabric, particles, PxClothFlags()); + PX_ASSERT(cloth); + + cloth->setSolverFrequency(60.0f); // don't know how to get target simulation frequency, just hardcode for now + + // add this cloth into the scene + getActiveScene().addActor(*cloth); + + // create render material + RenderMaterial* clothMaterial = createRenderMaterialFromTextureFile(textureFile); + + // create the render object in sample framework + createRenderObjectsFromCloth(*cloth, meshDesc, clothMaterial, uv, true, scale); + + return cloth; +} + +/////////////////////////////////////////////////////////////////////////////// +// create cloth from obj file +PxCloth* PhysXSample::createClothFromObj( + const char* objFileName, const PxTransform& pose, const char* textureFile) +{ + // create a mesh grid + SampleArray<PxVec4> vertices; + SampleArray<PxU32> primitives; + SampleArray<PxVec2> uvs; + PxClothMeshDesc meshDesc = Test::ClothHelpers::createMeshFromObj(objFileName, 1.0f, + PxQuat(PxIdentity), PxVec3(0.0f), vertices, primitives, uvs); + + return createClothFromMeshDesc(meshDesc, pose, PxVec3(0,0,-1), uvs.begin(), textureFile); +} + +/////////////////////////////////////////////////////////////////////////////// +// create cloth grid in XZ plane +PxCloth* PhysXSample::createGridCloth( + PxReal sizeX, PxReal sizeZ, PxU32 numX, PxU32 numZ, + const PxTransform& pose, const char* textureFile) +{ + // create a mesh grid + SampleArray<PxVec4> vertices; + SampleArray<PxU32> primitives; + SampleArray<PxVec2> uvs; + PxClothMeshDesc meshDesc = Test::ClothHelpers::createMeshGrid(PxVec3(sizeX, 0, 0), + PxVec3(0, 0, sizeZ), numX, numZ, vertices, primitives, uvs); + + return createClothFromMeshDesc(meshDesc, pose, PxVec3(0,0,-1), uvs.begin(), textureFile); +} + +#endif // PX_USE_CLOTH_API + +/////////////////////////////////////////////////////////////////////////////// +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; +} + +/////////////////////////////////////////////////////////////////////////////// +#if PX_USE_CLOTH_API + +void PhysXSample::createRenderObjectsFromCloth(const PxCloth& cloth, const PxClothMeshDesc& meshDesc, RenderMaterial* material, const PxVec2* uv, bool enableDebugRender, PxReal scale) +{ + RenderClothActor* clothActor = SAMPLE_NEW (RenderClothActor) + (*getRenderer(), cloth, meshDesc,uv, scale ); + if(!clothActor) + return; + + if (material) + clothActor->setRenderMaterial(material); + + mRenderClothActors.push_back(clothActor); + mRenderActors.push_back(clothActor); +} + +void PhysXSample::removeRenderClothActor(RenderClothActor& renderActor) +{ + std::vector<RenderBaseActor*>::iterator baseActorIter = std::find(mRenderActors.begin(), mRenderActors.end(), (RenderBaseActor*)(&renderActor)); + if(baseActorIter != mRenderActors.end()) + mRenderActors.erase(baseActorIter); + + std::vector<RenderClothActor*>::iterator clothActorIter = std::find(mRenderClothActors.begin(), mRenderClothActors.end(), &renderActor); + if(clothActorIter != mRenderClothActors.end()) + mRenderClothActors.erase(clothActorIter); +} + +#endif // PX_USE_CLOTH_API + +/////////////////////////////////////////////////////////////////////////////// + +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; + } + + 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<PxTransform>& localPoses, const std::vector<const PxGeometry*>& 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<PxTransform> localPoses; + std::vector<const PxGeometry*> geometries; + + PxToolkit::BasicRandom rnd(42); + + PxBoxGeometryAlloc* geoms = SAMPLE_NEW(PxBoxGeometryAlloc)[nbBoxes]; + + for(PxU32 i=0;i<nbBoxes;i++) + { + geoms[i].halfExtents = PxVec3(boxSize); + + PxTransform localPose; + rnd.unitRandomPt(localPose.p); + localPose.p.normalize(); + localPose.p *= amplitude; + rnd.unitRandomQuat(localPose.q); + + localPoses.push_back(localPose); + geometries.push_back(&geoms[i]); + } + PxRigidDynamic* actor = createCompound(pos, localPoses, geometries, vel, material, density); + DELETEARRAY(geoms); + return actor; +} + +/////////////////////////////////////////////////////////////////////////////// + +struct FindRenderActor +{ + FindRenderActor(PxShape* pxShape): mPxShape(pxShape) {} + bool operator() (const RenderBaseActor* actor) { return actor->getPhysicsShape() == mPxShape; } + PxShape* mPxShape; +}; + +void PhysXSample::removeRenderObject(RenderBaseActor* renderActor) +{ + std::vector<RenderBaseActor*>::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<RenderBaseActor*>::iterator renderIter = std::find_if(mRenderActors.begin(), mRenderActors.end(), findRenderActor); + if(renderIter != mRenderActors.end()) + { + unlink((*renderIter), shape, const_cast<PxRigidActor*>(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(mBufferedActiveTransforms[i].actor == actor) + { + mBufferedActiveTransforms[i] = mBufferedActiveTransforms[mActiveTransformCount-1]; + mActiveTransformCount--; + break; + } + } + mDeletedActors.push_back(const_cast<PxRigidActor*>(actor)); + } + + SAMPLE_FREE(shapes); +} + +////////////////////////////////////////////////////////////////////////// + +void PhysXSample::removeActor(PxRigidActor* actor) +{ + removeRenderActorsFromPhysicsActor(actor); + + std::vector<PxRigidActor*>::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; +} + +#if PX_USE_PARTICLE_SYSTEM_API + +RenderParticleSystemActor* PhysXSample::createRenderObjectFromParticleSystem(ParticleSystem& ps, RenderMaterial* material, bool useMeshInstancing, bool useFading, PxReal fadingPeriod, PxReal instancingScale) +{ + RenderParticleSystemActor* renderActor = SAMPLE_NEW(RenderParticleSystemActor)(*getRenderer(), &ps, useMeshInstancing, useFading, fadingPeriod, instancingScale); + if(material) renderActor->setRenderMaterial(material); + + mRenderActors.push_back(renderActor); + mRenderParticleSystemActors.push_back(renderActor); + return renderActor; +} + +void PhysXSample::addRenderParticleSystemActor(RenderParticleSystemActor& renderActor) +{ + mRenderActors.push_back(&renderActor); + mRenderParticleSystemActors.push_back(&renderActor); +} + +void PhysXSample::removeRenderParticleSystemActor(RenderParticleSystemActor& renderActor) +{ + std::vector<RenderBaseActor*>::iterator baseActorIter = std::find(mRenderActors.begin(), mRenderActors.end(), (RenderBaseActor*)(&renderActor)); + if(baseActorIter != mRenderActors.end()) + mRenderActors.erase(baseActorIter); + + std::vector<RenderParticleSystemActor*>::iterator psActorIter = std::find(mRenderParticleSystemActors.begin(), mRenderParticleSystemActors.end(), &renderActor); + if(psActorIter != mRenderParticleSystemActors.end()) + mRenderParticleSystemActors.erase(psActorIter); +} + +void PhysXSample::project(const PxVec3& v, int& x, int& y, float& depth) +{ + mPicking->project(v, x, y, depth); +} + +/////////////////////////////////////////////////////////////////////////////// +#if PX_USE_CLOTH_API +#endif // PX_USE_CLOTH_API + + +#endif // PX_USE_PARTICLE_SYSTEM_API + |