#include "../snippetrender/SnippetCamera.h"
#include "../snippetrender/SnippetRender.h"
#include "../snippetutils/SnippetUtils.h"
#include "PxPhysicsAPI.h"
#include "cudamanager/PxCudaContext.h"
#include "cudamanager/PxCudaContextManager.h"
#include "extensions/PxParticleExt.h"
#include <iostream>
#include <vector>
using namespace physx;
using namespace ExtGpu;
static PxDefaultAllocator gAllocator;
static PxDefaultErrorCallback gErrorCallback;
static PxFoundation* gFoundation = nullptr;
static PxPhysics* gPhysics = nullptr;
static PxDefaultCpuDispatcher* gDispatcher = nullptr;
static PxScene* gScene = nullptr;
static PxMaterial* gMaterial = nullptr;
static PxPBDParticleSystem* gParticleSystem = nullptr;
static PxParticleClothBuffer* gClothBuffer = nullptr;
static bool gIsRunning = false;
Snippets::Camera* sCamera = nullptr;
PxReal simTime = 0;
Snippets::SharedGLBuffer sPosBuffer;
void keyCallback(unsigned char key, const PxTransform&) {
switch (toupper(key)) {
case ' ':
gIsRunning = !gIsRunning;
break;
default:
break;
}
}
void stepPhysics(bool) {
if (gIsRunning) {
const PxReal dt = 1.0f / 60.0f;
gScene->simulate(dt);
gScene->fetchResults(true);
gScene->fetchResultsParticleSystem();
simTime = simTime + dt;
}
}
void onBeforeRenderParticles() {
if (gParticleSystem) {
PxVec4* positions = gClothBuffer->getPositionInvMasses();
const PxU32 numParticles = gClothBuffer->getNbActiveParticles();
PxCudaContextManager* cudaContextManager = gScene->getCudaContextManager();
cudaContextManager->acquireContext();
PxCudaContext* cudaContext = cudaContextManager->getCudaContext();
cudaContext->memcpyDtoH(sPosBuffer.map(), CUdeviceptr(positions), sizeof(PxVec4) * numParticles);
cudaContextManager->releaseContext();
// #if SHOW_SOLID_SDF_SLICE
// particleSystem->copySparseGridData(sSparseGridSolidSDFBufferD, PxSparseGridDataFlag::eGRIDCELL_SOLID_GRADIENT_AND_SDF);
// #endif
}
}
void renderParticles() {
sPosBuffer.unmap();
PxVec3 color(1.0f, 0.3f, 0.5);
Snippets::DrawPoints(sPosBuffer.vbo, sPosBuffer.size / sizeof(PxVec4), color, 2.0f);
Snippets::DrawFrame(PxVec3(0, 0, 0));
}
void renderCallback() {
onBeforeRenderParticles();
stepPhysics(true); // all render/ simulate in this function.
Snippets::startRender(sCamera);
PxU32 nbActors = gScene->getNbActors(PxActorTypeFlag::eRIGID_DYNAMIC | PxActorTypeFlag::eRIGID_STATIC);
if (nbActors) {
std::vector<PxRigidActor*> actors(nbActors);
gScene->getActors(PxActorTypeFlag::eRIGID_DYNAMIC | PxActorTypeFlag::eRIGID_STATIC, reinterpret_cast<PxActor**>(&actors[0]), nbActors);
Snippets::renderActors(&actors[0], static_cast<PxU32>(actors.size()), false);
}
renderParticles();
Snippets::finishRender();
}
void cleanupPhysics() {
PX_RELEASE(gScene);
PX_RELEASE(gDispatcher);
PX_RELEASE(gFoundation);
}
void exitCallback() {
delete sCamera;
}
static PX_FORCE_INLINE PxU32 id(PxU32 x, PxU32 y, PxU32 numY) {
return x * numY + y;
}
void initCloth(const PxU32 numX, const PxU32 numZ, const PxVec3& position = PxVec3(0, 0, 0), const PxReal spacing = 0.2f, const PxReal totalClothMass = 10.0f) {
PxCudaContextManager* cudaContextManager = gScene->getCudaContextManager();
if (cudaContextManager == NULL) {
std::cout << "No cuda manager." << std::endl;
}
const PxU32 numParticles = numX * numZ;
const PxU32 numSprings = (numX - 1) * (numZ - 1) * 4 + (numX - 1) + (numZ - 1);
const PxU32 numTrianges = (numX - 1) * (numZ - 1) * 2;
const PxReal resetOffset = spacing;
const PxReal stretchStiffness = 10000.0f;
const PxReal shearStiffness = 100.0f;
const PxReal springDamping = 0.001f;
// create PBDmaterial for cloth.
PxPBDMaterial* defaultMat = gPhysics->createPBDMaterial(0.8f, 0.05f, 1e+6f, 0.001f, 0.5f, 0.005f, 0.05f, 0.0f, 0.0f);
PxPBDParticleSystem* particleSystem = gPhysics->createPBDParticleSystem(*cudaContextManager);
gParticleSystem = particleSystem;
// set particle settings.
const PxReal particleMass = totalClothMass / numParticles;
particleSystem->setRestOffset(resetOffset);
particleSystem->setContactOffset(resetOffset + 0.02f);
particleSystem->setParticleContactOffset(resetOffset + 0.02f);
particleSystem->setSolidRestOffset(resetOffset);
particleSystem->setFluidRestOffset(0.0f);
gScene->addActor(*particleSystem);
PxU32 particlePhase = gParticleSystem->createPhase(defaultMat, PxParticlePhaseFlags(PxParticlePhaseFlag::eParticlePhaseSelfCollideFilter | PxParticlePhaseFlag::eParticlePhaseSelfCollide));
PxParticleClothBufferHelper* clothBuffers = PxCreateParticleClothBufferHelper(1, numTrianges, numSprings, numParticles, cudaContextManager);
PxU32* phase = cudaContextManager->allocPinnedHostBuffer<PxU32>(numParticles);
PxVec4* positionInvMass = cudaContextManager->allocPinnedHostBuffer<PxVec4>(numParticles);
PxVec4* velocity = cudaContextManager->allocPinnedHostBuffer<PxVec4>(numParticles);
PxReal x = position.x;
PxReal y = position.y;
PxReal z = position.z;
PxReal maxZ = z;
// define springs and triangles.
PxArray<PxParticleSpring> springs; // springs.
PxArray<PxU32> triangels; // triangles vertexs.
springs.reserve(numSprings);
triangels.reserve(numTrianges * 3);
// --j0,1,2------
// i0------------
// i1------------
// i2------------
for (PxU32 i = 0; i < numX; i++) {
for (PxU32 j = 0; j < numZ; j++) {
const PxU32 index = i * numZ + j;
PxVec4 pos(x, y, z, 1.0f / particleMass); // x,y,z,invMass
phase[index] = particlePhase;
velocity[index] = PxVec4(0.0f);
positionInvMass[index] = pos;
// vertical
if (i > 0) {
PxParticleSpring spring = {id(i - 1, j, numZ), id(i, j, numZ), spacing, stretchStiffness, springDamping, 0};
springs.pushBack(spring);
}
// horizontal
if (j > 0) {
PxParticleSpring spring = {id(i, j - 1, numZ), id(i, j, numZ), spacing, stretchStiffness, springDamping, 0};
springs.pushBack(spring);
}
// duijiao
if (i > 0 && j > 0) {
PxParticleSpring spring0 = {id(i - 1, j - 1, numZ), id(i, j, numZ), PxSqrt(2.0f) * spacing, shearStiffness, springDamping, 0};
PxParticleSpring spring1 = {id(i - 1, j, numZ), id(i, j - 1, numZ), PxSqrt(2.0f) * spacing, shearStiffness, springDamping, 0};
springs.pushBack(spring0);
springs.pushBack(spring1);
// ============================================
// (i-1,j-1)0---3(i-1,j)
// | / |
// (i ,j-1)1---2(i ,j)
// ============================================
// triangle: [0, 3, 1]
triangels.pushBack(id(i - 1, j - 1, numZ));
triangels.pushBack(id(i - 1, j, numZ));
triangels.pushBack(id(i, j - 1, numZ));
// triangle: [3, 1, 2]
triangels.pushBack(id(i - 1, j, numZ));
triangels.pushBack(id(i, j - 1, numZ));
triangels.pushBack(id(i, j, numZ));
}
z = z + spacing;
}
maxZ = z - spacing;
z = position.z;
x = x + spacing;
}
PX_ASSERT(numSprings == springs.size());
PX_ASSERT(numTrianges == triangels.size() / 3);
clothBuffers->addCloth(0.0f, 0.0f, 0.0f, triangels.begin(), numTrianges, springs.begin(), numSprings, positionInvMass, numParticles);
ExtGpu::PxParticleBufferDesc bufferDesc;
bufferDesc.maxParticles = numParticles;
bufferDesc.numActiveParticles = numParticles;
bufferDesc.positions = positionInvMass;
bufferDesc.velocities = velocity;
bufferDesc.phases = phase;
const PxParticleClothDesc& clothDesc = clothBuffers->getParticleClothDesc();
PxParticleClothPreProcessor* clothPreProcessor = PxCreateParticleClothPreProcessor(cudaContextManager);
PxPartitionedParticleCloth output;
clothPreProcessor->partitionSprings(clothDesc, output);
clothPreProcessor->release();
gClothBuffer = ExtGpu::PxCreateAndPopulateParticleClothBuffer(bufferDesc, clothDesc, output, cudaContextManager);
gParticleSystem->addParticleBuffer(gClothBuffer);
clothBuffers->release();
cudaContextManager->freePinnedHostBuffer(positionInvMass);
cudaContextManager->freePinnedHostBuffer(velocity);
cudaContextManager->freePinnedHostBuffer(phase);
}
struct Triangle {
PxU32 ind0;
PxU32 ind1;
PxU32 ind2;
};
PxTriangleMesh* initMesh() {
const PxU32 gridSize = 8;
const PxReal groudSize = 100.0f;
const PxReal gridStep = groudSize / (gridSize - 1);
const PxU32 numTriangles = (gridSize - 1) * (gridSize - 1) * 2;
PxVec3 verts[gridSize * gridSize];
Triangle indices[numTriangles];
for (PxU32 z = 0; z < gridSize; ++z) { // z-axis
for (PxU32 x = 0; x < gridSize; ++x) { // x-axis
verts[z * gridSize + x] = PxVec3(-groudSize * 0.5f + x * gridStep, 0.0f, -groudSize * 0.5f + z * gridStep);
}
}
for (PxU32 z = 0; z < (gridSize - 1); ++z) {
for (PxU32 x = 0; x < (gridSize - 1); ++x) {
Triangle& tri0 = indices[(z * (gridSize - 1) + x) * 2];
Triangle& tri1 = indices[(z * (gridSize - 1) + x) * 2 + 1];
// 0-----1
// | \ |
// 3-----2
// [0, 1, 2]
tri0.ind0 = z * gridSize + x + 1;
tri0.ind1 = z * gridSize + x;
tri0.ind2 = (z + 1) * gridSize + x + 1;
// [0, 2, 3]
tri1.ind0 = (z + 1) * gridSize + x + 1;
tri1.ind1 = z * gridSize + x;
tri1.ind2 = (z + 1) * gridSize + x;
}
}
PxTriangleMeshDesc meshDesc;
meshDesc.points.data = verts;
meshDesc.points.count = gridSize * gridSize;
meshDesc.points.stride = sizeof(PxVec3);
meshDesc.triangles.data = indices;
meshDesc.triangles.count = numTriangles;
meshDesc.triangles.stride = sizeof(Triangle);
PxCookingParams cookingParams(gPhysics->getTolerancesScale());
PxTriangleMesh* mesh = PxCreateTriangleMesh(cookingParams, meshDesc, gPhysics->getPhysicsInsertionCallback());
return mesh;
}
void initPhysics() {
gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback);
gPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(), true); // no pvd
gDispatcher = PxDefaultCpuDispatcherCreate(2);
gMaterial = gPhysics->createMaterial(0.5f, 0.5f, 0.6f);
// cuda environment.
PxCudaContextManager* cudaContextManager = NULL;
if (PxGetSuggestedCudaDeviceOrdinal(gFoundation->getErrorCallback()) >= 0) {
PxCudaContextManagerDesc cudaContextManagerDesc;
cudaContextManager = PxCreateCudaContextManager(*gFoundation, cudaContextManagerDesc, PxGetProfilerCallback());
if (cudaContextManager && !cudaContextManager->contextIsValid()) {
cudaContextManager->release();
cudaContextManager = NULL;
}
}
if (cudaContextManager == NULL) {
PxGetFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "Failed to initialize CUD!\n");
}
// create scene description.
PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
sceneDesc.gravity = PxVec3(0.0f, -9.8f, 0.0f);
sceneDesc.cpuDispatcher = gDispatcher;
sceneDesc.filterShader = PxDefaultSimulationFilterShader;
sceneDesc.cudaContextManager = cudaContextManager;
sceneDesc.staticStructure = PxPruningStructureType::eDYNAMIC_AABB_TREE;
sceneDesc.flags |= PxSceneFlag::eENABLE_PCM;
sceneDesc.flags |= PxSceneFlag::eENABLE_GPU_DYNAMICS;
sceneDesc.broadPhaseType = PxBroadPhaseType::eGPU;
sceneDesc.solverType = PxSolverType::eTGS;
gScene = gPhysics->createScene(sceneDesc);
// create background
// PxRigidStatic* groundPlane = PxCreatePlane(*gPhysics, PxPlane(0, 1, 0, 0), *gMaterial);
// gScene->addActor(*groundPlane);
// create groundMesh
PxTriangleMesh* mesh = initMesh();
PxTriangleMeshGeometry geom(mesh);
PxRigidStatic* groundMesh = gPhysics->createRigidStatic(PxTransform(PxVec3(0, -1, 0)));
PxRigidActorExt::createExclusiveShape(*groundMesh, geom, *gMaterial);
gScene->addActor(*groundMesh);
// create rigid body.
const PxReal size = PxReal(0.5);
const PxReal mass = PxReal(1.0);
PxShape* box_shape = gPhysics->createShape(PxBoxGeometry(size, size, size), *gMaterial);
for (size_t k = 0; k < 3; k++) {
for (size_t j = 0; j < 3; j++) {
for (size_t i = 0; i < 10; i++) {
if (k == 1 & j == 1) {
continue;
}
PxTransform localTm = PxTransform(PxVec3(3.0f * j, size * (2 * i + 1), 3.0f * k) - PxVec3(1, 0, 1));
PxRigidDynamic* body = gPhysics->createRigidDynamic(localTm);
PxRigidBodyExt::updateMassAndInertia(*body, mass);
body->attachShape(*box_shape);
gScene->addActor(*body);
}
}
}
box_shape->release();
// create shpere.
const PxReal radius = PxReal(1.0);
PxShape* sphere_shape = gPhysics->createShape(PxSphereGeometry(radius), *gMaterial);
for (size_t i = 0; i < 30; i++) {
PxTransform localTm = PxTransform(PxVec3(0.0f, radius * (2 * i + 1) + 8, 0.0f) * radius);
PxRigidDynamic* body = gPhysics->createRigidDynamic(localTm);
PxRigidBodyExt::updateMassAndInertia(*body, mass);
body->attachShape(*sphere_shape);
gScene->addActor(*body);
}
sphere_shape->release();
// create cloth shape.
const PxReal totalClothMass = 10.0f;
PxU32 numX = 250;
PxU32 numZ = 250;
PxReal spacing = 0.06f;
PxVec3 position = PxVec3(-0.5f * numX * spacing, 8.0f, -0.5f * numZ * spacing);
initCloth(numX, numZ, position, spacing, totalClothMass);
}
void allocParticleBuffers() {
PxCudaContextManager* cudaContextManager = gScene->getCudaContextManager();
PxU32 maxParticles = gClothBuffer->getMaxParticles();
sPosBuffer.initialize(cudaContextManager);
sPosBuffer.allocate(maxParticles * sizeof(PxVec4));
}
void renderLoop() {
sCamera = new Snippets::Camera(PxVec3(10.0f, 10.0f, 10.0f), PxVec3(-0.6f, -0.2f, -0.7f));
Snippets::setupDefault("SimpleDemo", sCamera, keyCallback, renderCallback, exitCallback);
initPhysics(); // init all physics variables and environment.
allocParticleBuffers(); // alloc memory for cloth render.
glutMainLoop(); // enetr the render loop.
}
int snippetMain(int, const char* const*) {
renderLoop(); // call the renderloop
cleanupPhysics();
return 0;
}