aboutsummaryrefslogtreecommitdiff
path: root/demo/scenes
diff options
context:
space:
mode:
authorMiles Macklin <[email protected]>2017-03-10 14:51:31 +1300
committerMiles Macklin <[email protected]>2017-03-10 14:51:31 +1300
commitad3d90fafe5ee79964bdfe1f1e0704c3ffcdfd5f (patch)
tree4cc6f3288363889d7342f7f8407c0251e6904819 /demo/scenes
downloadflex-ad3d90fafe5ee79964bdfe1f1e0704c3ffcdfd5f.tar.xz
flex-ad3d90fafe5ee79964bdfe1f1e0704c3ffcdfd5f.zip
Initial 1.1.0 binary release
Diffstat (limited to 'demo/scenes')
-rw-r--r--demo/scenes/adhesion.h50
-rw-r--r--demo/scenes/armadilloshower.h75
-rw-r--r--demo/scenes/bananas.h60
-rw-r--r--demo/scenes/bouyancy.h76
-rw-r--r--demo/scenes/bunnybath.h84
-rw-r--r--demo/scenes/ccdfluid.h107
-rw-r--r--demo/scenes/clothlayers.h81
-rw-r--r--demo/scenes/dambreak.h62
-rw-r--r--demo/scenes/darts.h59
-rw-r--r--demo/scenes/debris.h319
-rw-r--r--demo/scenes/deformables.h42
-rw-r--r--demo/scenes/envcloth.h77
-rw-r--r--demo/scenes/flag.h83
-rw-r--r--demo/scenes/fluidblock.h72
-rw-r--r--demo/scenes/fluidclothcoupling.h192
-rw-r--r--demo/scenes/forcefield.h92
-rw-r--r--demo/scenes/frictionmoving.h93
-rw-r--r--demo/scenes/frictionramp.h40
-rw-r--r--demo/scenes/gamemesh.h191
-rw-r--r--demo/scenes/googun.h72
-rw-r--r--demo/scenes/granularpile.h57
-rw-r--r--demo/scenes/granularshape.h36
-rw-r--r--demo/scenes/inflatable.h159
-rw-r--r--demo/scenes/initialoverlap.h30
-rw-r--r--demo/scenes/lighthouse.h59
-rw-r--r--demo/scenes/localspacecloth.h134
-rw-r--r--demo/scenes/localspacefluid.h127
-rw-r--r--demo/scenes/lowdimensionalshapes.h54
-rw-r--r--demo/scenes/melting.h70
-rw-r--r--demo/scenes/mixedpile.h221
-rw-r--r--demo/scenes/nonconvex.h47
-rw-r--r--demo/scenes/parachutingbunnies.h167
-rw-r--r--demo/scenes/pasta.h47
-rw-r--r--demo/scenes/plasticbody.h271
-rw-r--r--demo/scenes/plasticstack.h50
-rw-r--r--demo/scenes/player.h290
-rw-r--r--demo/scenes/potpourri.h117
-rw-r--r--demo/scenes/rayleightaylor.h143
-rw-r--r--demo/scenes/restitution.h27
-rw-r--r--demo/scenes/ridigbody.h139
-rw-r--r--demo/scenes/rigidfluidcoupling.h61
-rw-r--r--demo/scenes/rigidpile.h129
-rw-r--r--demo/scenes/rigidrotation.h56
-rw-r--r--demo/scenes/rockpool.h78
-rw-r--r--demo/scenes/sdfcollision.h54
-rw-r--r--demo/scenes/shapecollision.h101
-rw-r--r--demo/scenes/softbody.h325
-rw-r--r--demo/scenes/spherecloth.h85
-rw-r--r--demo/scenes/surfacetension.h57
-rw-r--r--demo/scenes/tearing.h132
-rw-r--r--demo/scenes/thinbox.h66
-rw-r--r--demo/scenes/trianglecollision.h47
-rw-r--r--demo/scenes/triggervolume.h81
-rw-r--r--demo/scenes/viscosity.h79
-rw-r--r--demo/scenes/waterballoon.h336
55 files changed, 5859 insertions, 0 deletions
diff --git a/demo/scenes/adhesion.h b/demo/scenes/adhesion.h
new file mode 100644
index 0000000..efbdec2
--- /dev/null
+++ b/demo/scenes/adhesion.h
@@ -0,0 +1,50 @@
+
+class Adhesion : public Scene
+{
+public:
+
+ Adhesion(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.1f;
+
+ g_params.radius = radius;
+
+ g_params.fluid = true;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.fluidRestDistance = g_params.radius*0.55f;
+ g_params.anisotropyScale = 3.0f / radius;
+ g_params.smoothing = 0.5f;
+ g_params.relaxationFactor = 1.f;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = 0.01f;
+
+ g_fluidColor = Vec4(0.0f, 0.8f, 0.2f, 1.0f);
+ //g_fluidColor = Vec4(0.7f, 0.6f, 0.6f, 0.2f);
+
+ g_params.dynamicFriction = 0.5f;
+ g_params.viscosity = 50.0f;
+ g_params.adhesion = 0.5f;
+ g_params.cohesion = 0.08f;
+ g_params.surfaceTension = 0.0f;
+
+ g_numExtraParticles = 64 * 1024;
+
+ AddBox(Vec3(1.0f, 1.5f, 0.1f), Vec3(0.0f, 1.5f, 0.0f));
+ AddBox(Vec3(1.0f, 0.1f, 6.0f), Vec3(-1.0f, 3.0f, 0.0f));
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.fluidRestDistance*2.f / g_dt);
+
+ g_params.numPlanes = 3;
+
+ // draw options
+ g_drawEllipsoids = true;
+
+ g_pause = false;
+ }
+
+ bool mViscous;
+};
diff --git a/demo/scenes/armadilloshower.h b/demo/scenes/armadilloshower.h
new file mode 100644
index 0000000..fdbf194
--- /dev/null
+++ b/demo/scenes/armadilloshower.h
@@ -0,0 +1,75 @@
+
+
+class ArmadilloShower : public Scene
+{
+public:
+
+ ArmadilloShower(const char* name, bool viscous) : Scene(name), mViscous(viscous) {}
+
+ virtual void Initialize()
+ {
+ float minSize = 0.5f;
+ float maxSize = 1.0f;
+
+ float radius = 0.1f;
+
+ for (int i=0; i < 5; i++)
+ AddRandomConvex(10, Vec3(i*2.0f, 0.0f, Randf(0.0f, 2.0f)), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi*10.0f));
+
+ g_params.radius = radius;
+
+ g_params.fluid = true;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.fluidRestDistance = g_params.radius*0.5f;
+ g_params.anisotropyScale = 20.0f;
+
+ if (mViscous)
+ {
+ g_fluidColor = Vec4(0.0f, 0.8f, 0.2f, 1.0f);
+
+ g_params.dynamicFriction = 0.5f;
+ g_params.viscosity = 10.85f;
+ g_params.cohesion = 0.25f;
+ }
+ else
+ {
+ g_params.dynamicFriction = 0.025f;
+ g_params.viscosity = 0.05f;
+ }
+
+ g_numExtraParticles = 64*1024;
+
+ g_diffuseScale = 1.0f;
+
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/armadillo.ply").c_str(), 128);
+ AddSDF(sdf, Vec3(2.0f, 0.0f, -1.0f), Quat(), 2.0f);
+
+ Vec3 meshLower, meshUpper;
+ g_mesh->GetBounds(meshLower, meshUpper);
+
+ Emitter e1;
+ e1.mDir = Vec3(-1.0f, 0.0f, 0.0f);
+ e1.mRight = Vec3(0.0f, 0.0f, 1.0f);
+ e1.mPos = Vec3(-1.0f, 0.15f, 0.50f) + meshLower + Vec3(meshUpper.x, meshUpper.y*0.75f, meshUpper.z*0.5f);
+ e1.mSpeed = (g_params.radius*0.5f/g_dt)*2.0f; // 2 particle layers per-frame
+ e1.mEnabled = true;
+
+ Emitter e2;
+ e2.mDir = Vec3(1.0f, 0.0f, 0.0f);
+ e2.mRight = Vec3(0.0f, 0.0f, -1.0f);
+ e2.mPos = Vec3(-1.0f, 0.15f, 0.50f) + meshLower + Vec3(0.0f, meshUpper.y*0.75f, meshUpper.z*0.5f);
+ e2.mSpeed = (g_params.radius*0.5f/g_dt)*2.0f; // 2 particle layers per-frame
+ e2.mEnabled = true;
+
+ g_emitters.push_back(e1);
+ g_emitters.push_back(e2);
+
+ g_emit = true;
+
+ // draw options
+ g_drawEllipsoids = true;
+ }
+
+ bool mViscous;
+}; \ No newline at end of file
diff --git a/demo/scenes/bananas.h b/demo/scenes/bananas.h
new file mode 100644
index 0000000..b404a1e
--- /dev/null
+++ b/demo/scenes/bananas.h
@@ -0,0 +1,60 @@
+
+
+class BananaPile : public Scene
+{
+public:
+
+ BananaPile(const char* name) : Scene(name)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ float s = 1.0f;
+
+ Vec3 lower(0.0f, 1.0f + g_params.radius*0.25f, 0.0f);
+
+ int dimx = 3;
+ int dimy = 40;
+ int dimz = 2;
+
+ float radius = g_params.radius;
+ int group = 0;
+
+ // create a basic grid
+ for (int x = 0; x < dimx; ++x)
+ {
+ for (int y = 0; y < dimy; ++y)
+ {
+ for (int z = 0; z < dimz; ++z)
+ {
+ CreateParticleShape(GetFilePathByPlatform("../../data/banana.obj").c_str(), lower + (s*1.1f)*Vec3(float(x), y*0.4f, float(z)), Vec3(s), 0.0f, radius*0.95f, Vec3(0.0f), 1.0f, true, 0.8f, NvFlexMakePhase(group++, 0), true, radius*0.1f, 0.0f, 0.0f, 1.25f*Vec4(0.875f, 0.782f, 0.051f, 1.0f));
+ }
+ }
+ }
+
+ AddPlinth();
+
+ g_numSubsteps = 3;
+ g_params.numIterations = 2;
+
+ g_params.radius *= 1.0f;
+ g_params.dynamicFriction = 0.25f;
+ g_params.dissipation = 0.03f;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.sleepThreshold = g_params.radius*0.2f;
+ g_params.shockPropagation = 2.5f;
+ g_params.restitution = 0.55f;
+ g_params.damping = 0.25f;
+
+ // draw options
+ g_drawPoints = false;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.radius*2.0f / g_dt);
+ }
+
+ virtual void Update()
+ {
+ }
+};
diff --git a/demo/scenes/bouyancy.h b/demo/scenes/bouyancy.h
new file mode 100644
index 0000000..816146f
--- /dev/null
+++ b/demo/scenes/bouyancy.h
@@ -0,0 +1,76 @@
+
+
+
+class Buoyancy : public Scene
+{
+public:
+
+ Buoyancy(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.1f;
+ float restDistance = radius*0.5f;
+ int group = 0;
+
+ int n = 3;
+ float spacing = 64*restDistance*0.9f/(2.0f*n);
+ float sampling = restDistance*0.8f;
+ Vec3 size = sampling*12.0f;
+
+ const float mass[] = {1.0f, 0.25f, 0.005f };
+
+ for (int j=0; j < 1; ++j)
+ for (int i=0; i < n; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/sphere.ply").c_str(), Vec3(spacing - 0.5f*size.x + i*spacing * 2, 2.0f + j*size.y*1.2f, 0.6f), size, 0.0f, sampling, Vec3(0.0f, 0.0f, 0.0f), mass[i], true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0001f);
+
+ g_numSolidParticles = g_buffers->positions.size();
+
+ int x = 64;
+ int y = 20;
+ int z = 32;
+
+ CreateParticleGrid(Vec3(0.0f), x, y, z, restDistance*0.9f, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid));
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.1f;
+ g_params.fluid = true;
+ g_params.viscosity = 2.0f;
+ g_params.numIterations = 4;
+ g_params.vorticityConfinement = 180.f;
+ g_params.anisotropyScale = 20.0f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.numPlanes = 5;
+ g_params.cohesion = 0.001625f;
+ g_params.collisionDistance = restDistance;
+ g_params.solidPressure = 0.1f;
+ g_params.restitution = 0.0f;
+ g_params.relaxationFactor = 1.0f;
+ g_params.relaxationMode = eNvFlexRelaxationLocal;
+
+ g_numSubsteps = 2;
+
+ g_maxDiffuseParticles = 64*1024;
+ g_diffuseScale = 0.25f;
+ g_diffuseShadow = false;
+ g_diffuseColor = 1.5f;
+ g_diffuseMotionScale = 1.5f;
+ g_params.diffuseBallistic = 35;
+ g_params.diffuseThreshold *= 0.1f;
+
+ g_drawPlaneBias = g_params.collisionDistance*1.5f;
+
+ g_lightDistance *= 0.65f;
+
+ g_fluidColor = Vec4(0.2f, 0.6f, 0.9f, 1.0f);
+
+ // draw options
+ g_drawDensity = true;
+ g_drawDiffuse = true;
+ g_drawEllipsoids = true;
+ g_drawPoints = false;
+
+ g_warmup = true;
+ }
+
+};
diff --git a/demo/scenes/bunnybath.h b/demo/scenes/bunnybath.h
new file mode 100644
index 0000000..db9e6ac
--- /dev/null
+++ b/demo/scenes/bunnybath.h
@@ -0,0 +1,84 @@
+
+class BunnyBath : public Scene
+{
+public:
+
+ BunnyBath(const char* name, bool dam) : Scene(name), mDam(dam) {}
+
+ void Initialize()
+ {
+ float radius = 0.1f;
+
+ // deforming bunny
+ float s = radius*0.5f;
+ float m = 0.25f;
+ int group = 1;
+
+ CreateParticleShape(GetFilePathByPlatform("../../data/bunny.ply").c_str(), Vec3(4.0f, 0.0f, 0.0f), 0.5f, 0.0f, s, Vec3(0.0f, 0.0f, 0.0f), m, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), Vec3(4.0f, 0.0f, 1.0f), 0.45f, 0.0f, s, Vec3(0.0f, 0.0f, 0.0f), m, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+ CreateParticleShape(GetFilePathByPlatform("../../data/bunny.ply").c_str(), Vec3(3.0f, 0.0f, 0.0f), 0.5f, 0.0f, s, Vec3(0.0f, 0.0f, 0.0f), m, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+ CreateParticleShape(GetFilePathByPlatform("../../data/sphere.ply").c_str(), Vec3(3.0f, 0.0f, 1.0f), 0.45f, 0.0f, s, Vec3(0.0f, 0.0f, 0.0f), m, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+ CreateParticleShape(GetFilePathByPlatform("../../data/bunny.ply").c_str(), Vec3(2.0f, 0.0f, 1.0f), 0.5f, 0.0f, s, Vec3(0.0f, 0.0f, 0.0f), m, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), Vec3(2.0f, 0.0f, 0.0f), 0.45f, 0.0f, s, Vec3(0.0f, 0.0f, 0.0f), m, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+
+ g_numSolidParticles = g_buffers->positions.size();
+
+ float restDistance = radius*0.55f;
+
+ if (mDam)
+ {
+ CreateParticleGrid(Vec3(0.0f, 0.0f, 0.6f), 24, 48, 24, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), 0.005f);
+ g_lightDistance *= 0.5f;
+ }
+
+ g_sceneLower = Vec3(0.0f);
+
+ g_numSubsteps = 2;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.01f;
+ g_params.fluid = true;
+ g_params.viscosity = 2.0f;
+ g_params.numIterations = 4;
+ g_params.vorticityConfinement = 40.0f;
+ g_params.anisotropyScale = 30.0f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.solidPressure = 0.f;
+ g_params.relaxationFactor = 0.0f;
+ g_params.cohesion = 0.02f;
+ g_params.collisionDistance = 0.01f;
+
+ g_maxDiffuseParticles = 64*1024;
+ g_diffuseScale = 0.5f;
+
+ g_fluidColor = Vec4(0.113f, 0.425f, 0.55f, 1.f);
+
+ Emitter e1;
+ e1.mDir = Vec3(1.0f, 0.0f, 0.0f);
+ e1.mRight = Vec3(0.0f, 0.0f, -1.0f);
+ e1.mPos = Vec3(radius, 1.f, 0.65f);
+ e1.mSpeed = (restDistance/g_dt)*2.0f; // 2 particle layers per-frame
+ e1.mEnabled = true;
+
+ g_emitters.push_back(e1);
+
+ g_numExtraParticles = 48*1024;
+
+ g_lightDistance = 1.8f;
+
+ g_params.numPlanes = 5;
+
+ g_waveFloorTilt = 0.0f;
+ g_waveFrequency = 1.5f;
+ g_waveAmplitude = 2.0f;
+
+ g_warmup = true;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ g_drawDiffuse = true;
+ }
+
+ bool mDam;
+};
diff --git a/demo/scenes/ccdfluid.h b/demo/scenes/ccdfluid.h
new file mode 100644
index 0000000..356eec7
--- /dev/null
+++ b/demo/scenes/ccdfluid.h
@@ -0,0 +1,107 @@
+
+class CCDFluid : public Scene
+{
+public:
+
+ CCDFluid (const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ const float radius = 0.05f;
+ const float restDistance = radius*0.6f;
+
+ int dx = int(ceilf(1.f / restDistance));
+ int dy = int(ceilf(1.f / restDistance));
+ int dz = int(ceilf(1.f / restDistance));
+
+ CreateParticleGrid(Vec3(0.0f, 1.0f, 0.0f), dx, dy, dz, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Vec3 center = (lower+upper)*0.5f;
+
+ //Mesh* shape = ImportMesh("../../data/box.ply");
+ //shape->Transform(ScaleMatrix(Vec3(2.0f)));
+
+ Mesh* shape = ImportMesh("../../data/torus.obj");
+ shape->Transform(ScaleMatrix(Vec3(0.7f)));
+
+ //Mesh* box = ImportMesh("../../data/sphere.ply");
+ //box->Transform(TranslationMatrix(Point3(0.0f, 0.1f, 0.0f))*ScaleMatrix(Vec3(1.5f)));
+
+ // invert box faces
+ for (int i=0; i < int(shape->GetNumFaces()); ++i)
+ swap(shape->m_indices[i*3+0], shape->m_indices[i*3+1]);
+
+ shape->CalculateNormals();
+
+ // shift into torus interior
+ for (int i=0; i < g_buffers->positions.size(); ++i)
+ (Vec3&)(g_buffers->positions[i]) -= Vec3(2.1f, 0.0f, 0.0f);
+
+ mesh = CreateTriangleMesh(shape);
+ AddTriangleMesh(mesh, Vec3(center), Quat(), 1.0f);
+
+ // initialize our moving frame to the center of the box
+ newOffset = g_buffers->shapePositions[0].x;
+ newRotation = 0.0f;
+ rotationSpeed = 0.0f;
+
+ g_numSubsteps = 2;
+
+ g_params.fluid = true;
+ g_params.radius = radius;
+ g_params.fluidRestDistance = restDistance;
+ g_params.dynamicFriction = 0.1f;
+ g_params.restitution = 0.0f;
+ g_params.shapeCollisionMargin = 0.05f;
+ //g_params.maxAcceleration = 50.0f;
+ g_params.maxSpeed = g_numSubsteps*restDistance/g_dt;
+ g_params.collisionDistance = restDistance;
+ //g_params.shapeCollisionMargin = 0.0001f;
+
+ g_params.numIterations = 3;
+ //g_params.relaxationFactor = 0.5f;
+
+ g_params.smoothing = 0.4f;
+ g_params.anisotropyScale = 3.0f / radius;
+
+ g_params.viscosity = 0.001f;
+ g_params.cohesion = 0.05f;
+ g_params.surfaceTension = 0.0f;
+
+ // draw options
+ g_drawPoints = true;
+ g_drawEllipsoids = false;
+ g_drawDiffuse = true;
+ }
+
+ virtual void DoGui()
+ {
+ imguiSlider("Linear", &newOffset, 0.0f, 2.0f, 0.0001f);
+ imguiSlider("Rotation", &rotationSpeed, 0.0f, 10.0f, 0.1f);
+ }
+
+ virtual void Update()
+ {
+ g_buffers->shapePrevPositions[0].x = g_buffers->shapePositions[0].x;
+ g_buffers->shapePositions[0].x = newOffset;
+
+ newRotation += rotationSpeed*g_dt;
+
+ g_buffers->shapePrevRotations[0] = g_buffers->shapeRotations[0];
+ g_buffers->shapeRotations[0] = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), newRotation);
+
+ // update previous transform of the disc
+ UpdateShapes();
+ }
+
+ float newOffset;
+ float newRotation;
+
+ float rotationSpeed;
+
+ NvFlexTriangleMeshId mesh;
+};
+ \ No newline at end of file
diff --git a/demo/scenes/clothlayers.h b/demo/scenes/clothlayers.h
new file mode 100644
index 0000000..eb1a01f
--- /dev/null
+++ b/demo/scenes/clothlayers.h
@@ -0,0 +1,81 @@
+
+
+class ClothLayers : public Scene
+{
+public:
+
+ ClothLayers(const char* name) :
+ Scene(name) {}
+
+ virtual void Initialize()
+ {
+
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.8f;
+ float shearStiffness = 0.5f;
+
+ int dimx = 64;
+ int dimz = 64;
+ float radius = 0.05f;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide);
+
+#if 1
+ CreateSpringGrid(Vec3(-0.6f, 2.9f, -0.6f), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+ CreateSpringGrid(Vec3(-0.6f, 2.6f, -0.6f), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+ CreateSpringGrid(Vec3(-0.6f, 2.3f, -0.6f), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+ CreateSpringGrid(Vec3(-0.6f, 2.0f, -0.6f), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Mesh* sphere = ImportMesh(GetFilePathByPlatform("../../data/sphere.ply").c_str());
+ sphere->Normalize();
+
+ NvFlexTriangleMeshId mesh = CreateTriangleMesh(sphere);
+ AddTriangleMesh(mesh, Vec3(), Quat(), 2.0f);
+
+ delete sphere;
+#else
+ // This scene can cause the cloth to bounce
+ // Might need to run it a few times to repro
+ CreateSpringGrid(Vec3(-0.6f, 2.9f, -0.6f), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Mesh* disc = CreateDiscMesh(2.0f, 300);
+ disc->m_positions[0].y -= 0.25f;
+ disc->CalculateNormals();
+ NvFlexTriangleMeshId mesh = CreateTriangleMesh(disc);
+ AddTriangleMesh(mesh, Vec3(0.0f, 2.88f, 1.0f), Quat(), 1.0f);
+ delete disc;
+
+ Mesh* disc1 = CreateDiscMesh(2.0f, 250);
+ disc1->m_positions[0].y -= 0.25f;
+ disc1->CalculateNormals();
+ NvFlexTriangleMeshId mesh1 = CreateTriangleMesh(disc1);
+ AddTriangleMesh(mesh1, Vec3(1.0f, 1.5f, 0.0f), Quat(), 1.0f);
+ delete disc1;
+#endif
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.1625f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.05f;
+ g_params.collisionDistance = radius;
+ g_params.shapeCollisionMargin = radius*0.1f;
+ g_params.relaxationFactor = 1.3f;
+
+ g_numSubsteps = 3;
+
+
+ g_windStrength = 0.0f;
+
+ // draw options
+ g_drawPoints = true;
+ g_drawSprings = false;
+ }
+};
+
diff --git a/demo/scenes/dambreak.h b/demo/scenes/dambreak.h
new file mode 100644
index 0000000..c2f9a40
--- /dev/null
+++ b/demo/scenes/dambreak.h
@@ -0,0 +1,62 @@
+
+
+class DamBreak : public Scene
+{
+public:
+
+ DamBreak(const char* name, float radius) : Scene(name), mRadius(radius) {}
+
+ virtual void Initialize()
+ {
+ const float radius = mRadius;
+ const float restDistance = mRadius*0.65f;
+
+ int dx = int(ceilf(1.0f / restDistance));
+ int dy = int(ceilf(2.0f / restDistance));
+ int dz = int(ceilf(1.0f / restDistance));
+
+ CreateParticleGrid(Vec3(0.0f, restDistance, 0.0f), dx, dy, dz, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+
+ g_sceneLower = Vec3(0.0f, 0.0f, -0.5f);
+ g_sceneUpper = Vec3(3.0f, 0.0f, -0.5f);
+
+ g_numSubsteps = 2;
+
+ g_params.fluid = true;
+ g_params.radius = radius;
+ g_params.fluidRestDistance = restDistance;
+ g_params.dynamicFriction = 0.f;
+ g_params.restitution = 0.001f;
+
+ g_params.numIterations = 3;
+ g_params.relaxationFactor = 1.0f;
+
+ g_params.smoothing = 0.4f;
+ g_params.anisotropyScale = 3.0f / radius;
+
+ g_params.viscosity = 0.001f;
+ g_params.cohesion = 0.1f;
+ g_params.vorticityConfinement = 80.0f;
+ g_params.surfaceTension = 0.0f;
+
+ g_params.numPlanes = 5;
+
+ // limit velocity to CFL condition
+ g_params.maxSpeed = 0.5f*radius*g_numSubsteps / g_dt;
+
+ g_maxDiffuseParticles = 0;
+
+ g_fluidColor = Vec4(0.113f, 0.425f, 0.55f, 1.0f);
+
+ g_waveFrequency = 1.0f;
+ g_waveAmplitude = 2.0f;
+ g_waveFloorTilt = 0.0f;
+
+ // draw options
+ g_drawPoints = true;
+ g_drawEllipsoids = false;
+ g_drawDiffuse = true;
+ }
+
+ float mRadius;
+};
diff --git a/demo/scenes/darts.h b/demo/scenes/darts.h
new file mode 100644
index 0000000..594a2ed
--- /dev/null
+++ b/demo/scenes/darts.h
@@ -0,0 +1,59 @@
+
+class Darts : public Scene
+{
+public:
+
+ Darts(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float radius = 0.1f;
+ int phase = NvFlexMakePhase(0, 0);
+
+ if (1)
+ {
+ Vec3 v = Vec3(10.0f, 0.0f, 0.0f);
+
+ float y = 8.0f;
+
+ g_buffers->positions.push_back(Vec4(0.0f, y, 0.0f, 1.0f));
+ g_buffers->velocities.push_back(v);
+ g_buffers->phases.push_back(phase);
+
+ g_buffers->positions.push_back(Vec4(-1.0f, y, -0.5f, 0.9f));
+ g_buffers->velocities.push_back(v);
+ g_buffers->phases.push_back(phase);
+
+ g_buffers->positions.push_back(Vec4(-1.0f, y, 0.5f, 0.9f));
+ g_buffers->velocities.push_back(v);
+ g_buffers->phases.push_back(phase);
+
+ g_buffers->triangles.push_back(0);
+ g_buffers->triangles.push_back(1);
+ g_buffers->triangles.push_back(2);
+ g_buffers->triangleNormals.push_back(0.0f);
+
+ CreateSpring(0, 1, 1.0f);
+ CreateSpring(1, 2, 1.0f);
+ CreateSpring(2, 0, 1.0f);
+
+ g_buffers->positions[0].y -= radius*2.5f;
+ //g_buffers->positions[0].x = 1.0f;
+ //g_buffers->positions[1].y += radius;
+ }
+
+ g_params.drag = 0.01f;
+ g_params.lift = 0.1f;
+ g_params.dynamicFriction = 0.25f;
+ //g_params.gravity[1] = 0.0f;
+
+ g_drawPoints = false;
+ }
+
+ void Update()
+ {
+ g_params.wind[0] = 0.0f;
+ g_params.wind[1] = 0.0f;
+ g_params.wind[2] = 0.0f;
+ }
+};
diff --git a/demo/scenes/debris.h b/demo/scenes/debris.h
new file mode 100644
index 0000000..39013ff
--- /dev/null
+++ b/demo/scenes/debris.h
@@ -0,0 +1,319 @@
+
+class RigidDebris : public Scene
+{
+public:
+
+ RigidDebris(const char* name) : Scene(name) {}
+
+ struct Instance
+ {
+ Vec3 mTranslation;
+ Quat mRotation;
+ float mLifetime;
+
+ int mGroup;
+ int mParticleOffset;
+
+ int mMeshIndex;
+ };
+
+ struct MeshBatch
+ {
+ GpuMesh* mMesh;
+ NvFlexExtAsset* mAsset;
+
+ std::vector<Matrix44> mInstanceTransforms;
+ };
+
+ struct MeshAsset
+ {
+ const char* file;
+ float scale;
+ };
+
+ void Initialize()
+ {
+ float radius = 0.1f;
+
+ const int numMeshes = 8;
+ MeshAsset meshes[numMeshes] =
+ {
+ { "../../data/rocka.ply", 0.2f },
+ { "../../data/box.ply", 0.1f },
+ { "../../data/torus.obj", 0.3f },
+ { "../../data/rockd.ply", 0.2f },
+ { "../../data/banana.obj", 0.3f },
+ { "../../data/rocka.ply", 0.2f },
+ { "../../data/box.ply", 0.1f },
+ { "../../data/rockd.ply", 0.2f },
+ //"../../data/rockf.ply"
+ };
+
+ for (int i = 0; i < numMeshes; ++i)
+ {
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform(meshes[i].file).c_str());
+ mesh->Normalize(meshes[i].scale);
+
+ const float spacing = radius*0.5f;
+
+ MeshBatch b;
+ b.mAsset = NvFlexExtCreateRigidFromMesh((float*)&mesh->m_positions[0], int(mesh->m_positions.size()), (int*)&mesh->m_indices[0], mesh->m_indices.size(), spacing, -spacing*0.5f);
+ b.mMesh = CreateGpuMesh(mesh);
+
+ mBatches.push_back(b);
+ }
+
+ Mesh* level = ImportMeshFromBin(GetFilePathByPlatform("../../data/testzone.bin").c_str());
+ level->Transform(TranslationMatrix(Point3(-10.0f, 0.0f, 10.0f)));
+
+ NvFlexTriangleMeshId mesh = CreateTriangleMesh(level);
+ AddTriangleMesh(mesh, Vec3(), Quat(), 1.0f);
+
+ delete level;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.6f;
+ g_params.staticFriction = 0.35f;
+ g_params.particleFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 4;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.0f;
+ g_params.lift = 0.0f;
+ g_params.numPlanes = 0;
+ g_params.collisionDistance = radius*0.5f;
+ g_params.particleCollisionMargin = radius*0.25f;
+
+ g_numExtraParticles = 32000;
+
+ g_drawPoints = false;
+
+ g_numSubsteps = 2;
+
+ g_lightDistance *= 3.0f;
+
+ mAttractForce = 0.0f;
+
+ mGroupCounter = 0;
+ mInstances.resize(0);
+ }
+
+ void Update()
+ {
+ // copy transforms out
+ for (int i = 0; i < int(mInstances.size()); ++i)
+ {
+ mInstances[i].mTranslation = g_buffers->rigidTranslations[i];
+ mInstances[i].mRotation = g_buffers->rigidRotations[i];
+ }
+
+ if (g_emit)
+ {
+ // emit new debris
+ int numToEmit = 1;//Rand()%8;
+
+ int particleOffset = NvFlexGetActiveCount(g_flex);
+
+ for (int i = 0; i < numToEmit; ++i)
+ {
+ // choose a random mesh to emit
+ const int meshIndex = Rand() % mBatches.size();
+
+ NvFlexExtAsset* asset = mBatches[meshIndex].mAsset;
+
+ // check we can fit in the container
+ if (int(g_buffers->positions.size()) - particleOffset < asset->numParticles)
+ break;
+
+ Instance inst;
+ inst.mLifetime = 1000.0f;// Randf(5.0f, 60.0f);
+ inst.mParticleOffset = particleOffset;
+ inst.mRotation = QuatFromAxisAngle(UniformSampleSphere(), Randf()*k2Pi);
+
+ float spread = 0.2f;
+ inst.mTranslation = g_emitters[0].mPos + Vec3(Randf(-spread, spread), Randf(-spread, spread), 0.0f);
+ inst.mMeshIndex = meshIndex;
+
+ Vec3 linearVelocity = g_emitters[0].mDir*15.0f;//*Randf(5.0f, 10.0f);//Vec3(Randf(0.0f, 10.0f), 0.0f, 0.0f);
+ Vec3 angularVelocity = Vec3(UniformSampleSphere()*Randf()*k2Pi);
+
+ inst.mGroup = mGroupCounter++;
+
+ const int phase = NvFlexMakePhase(inst.mGroup, 0);
+
+ // generate initial particle positions
+ for (int j = 0; j < asset->numParticles; ++j)
+ {
+ Vec3 localPos = Vec3(&asset->particles[j * 4]) - Vec3(&asset->shapeCenters[0]);
+
+ g_buffers->positions[inst.mParticleOffset + j] = Vec4(inst.mTranslation + inst.mRotation*localPos, 1.0f);
+ g_buffers->velocities[inst.mParticleOffset + j] = linearVelocity + Cross(angularVelocity, localPos);
+ g_buffers->phases[inst.mParticleOffset + j] = phase;
+ }
+
+ particleOffset += asset->numParticles;
+
+ mInstances.push_back(inst);
+ }
+ }
+
+ // destroy old debris pieces
+ for (int i = 0; i < int(mInstances.size());)
+ {
+ Instance& inst = mInstances[i];
+
+ inst.mLifetime -= g_dt;
+
+ if (inst.mLifetime <= 0.0f)
+ {
+ inst = mInstances.back();
+ mInstances.pop_back();
+ }
+ else
+ {
+ ++i;
+ }
+ }
+
+ // compact instances
+ static std::vector<Vec4> particles(g_buffers->positions.size());
+ static std::vector<Vec3> velocities(g_buffers->velocities.size());
+ static std::vector<int> phases(g_buffers->phases.size());
+
+ g_buffers->rigidTranslations.resize(0);
+ g_buffers->rigidRotations.resize(0);
+ g_buffers->rigidCoefficients.resize(0);
+ g_buffers->rigidIndices.resize(0);
+ g_buffers->rigidLocalPositions.resize(0);
+ g_buffers->rigidOffsets.resize(0);
+
+ // start index
+ g_buffers->rigidOffsets.push_back(0);
+
+ // clear mesh batches
+ for (int i = 0; i < int(mBatches.size()); ++i)
+ mBatches[i].mInstanceTransforms.resize(0);
+
+ numActive = 0;
+
+ for (int i = 0; i < int(mInstances.size()); ++i)
+ {
+ Instance& inst = mInstances[i];
+
+ NvFlexExtAsset* asset = mBatches[inst.mMeshIndex].mAsset;
+
+ for (int j = 0; j < asset->numParticles; ++j)
+ {
+ particles[numActive + j] = g_buffers->positions[inst.mParticleOffset + j];
+ velocities[numActive + j] = g_buffers->velocities[inst.mParticleOffset + j];
+ phases[numActive + j] = g_buffers->phases[inst.mParticleOffset + j];
+ }
+
+ g_buffers->rigidCoefficients.push_back(1.0f);
+ g_buffers->rigidTranslations.push_back(inst.mTranslation);
+ g_buffers->rigidRotations.push_back(inst.mRotation);
+
+ for (int j = 0; j < asset->numShapeIndices; ++j)
+ {
+ g_buffers->rigidLocalPositions.push_back(Vec3(&asset->particles[j * 4]) - Vec3(&asset->shapeCenters[0]));
+ g_buffers->rigidIndices.push_back(asset->shapeIndices[j] + numActive);
+ }
+
+ g_buffers->rigidOffsets.push_back(g_buffers->rigidIndices.size());
+
+ mInstances[i].mParticleOffset = numActive;
+
+ // Draw transform
+ Matrix44 xform = TranslationMatrix(Point3(inst.mTranslation - inst.mRotation*Vec3(asset->shapeCenters)))*RotationMatrix(inst.mRotation);
+ mBatches[inst.mMeshIndex].mInstanceTransforms.push_back(xform);
+
+ numActive += asset->numParticles;
+ }
+
+ // update particle buffers
+ g_buffers->positions.assign(&particles[0], particles.size());
+ g_buffers->velocities.assign(&velocities[0], velocities.size());
+ g_buffers->phases.assign(&phases[0], phases.size());
+
+ // rebuild active indices
+ g_buffers->activeIndices.resize(numActive);
+ for (int i = 0; i < numActive; ++i)
+ g_buffers->activeIndices[i] = i;
+
+ if (mAttractForce != 0.0f)
+ {
+ const Vec3 forward(-sinf(g_camAngle.x)*cosf(g_camAngle.y), sinf(g_camAngle.y), -cosf(g_camAngle.x)*cosf(g_camAngle.y));
+
+ Vec3 attractPos = g_camPos + forward*5.0f;
+ float invRadius = 1.0f / 5.0f;
+
+ for (int i = 0; i < int(g_buffers->velocities.size()); ++i)
+ {
+ Vec3 dir = Vec3(g_buffers->positions[i]) - attractPos;
+ float d = Length(dir);
+
+ g_buffers->velocities[i] += Normalize(dir)*Randf(0.0, 1.0f)*mAttractForce*Max(0.0f, 1.0f - d*invRadius);
+ }
+ }
+ }
+
+ virtual void PostInitialize()
+ {
+ g_sceneLower = Vec3(-5.0f, 0.0f, 0.0f);
+ g_sceneUpper = g_sceneLower + Vec3(10.0f, 10.0f, 5.0f);
+ }
+
+ virtual void Sync()
+ {
+ NvFlexSetRigids(g_flex, g_buffers->rigidOffsets.buffer, g_buffers->rigidIndices.buffer, g_buffers->rigidLocalPositions.buffer, g_buffers->rigidLocalNormals.buffer, g_buffers->rigidCoefficients.buffer, g_buffers->rigidRotations.buffer, g_buffers->rigidTranslations.buffer, g_buffers->rigidOffsets.size() - 1, g_buffers->rigidIndices.size());
+ }
+
+ virtual void KeyDown(int key)
+ {
+ if (key == 'B')
+ {
+ float bombStrength = 10.0f;
+
+ Vec3 bombPos = g_emitters[0].mPos + g_emitters[0].mDir*5.0f;
+ bombPos.y -= 5.0f;
+
+ for (int i = 0; i < int(g_buffers->velocities.size()); ++i)
+ {
+ Vec3 dir = Vec3(g_buffers->positions[i]) - bombPos;
+
+ g_buffers->velocities[i] += Normalize(dir)*bombStrength*Randf(0.0, 1.0f);
+ }
+ }
+
+ if (key == 'V')
+ {
+ if (mAttractForce == 0.0f)
+ mAttractForce = -1.5f;
+ else
+ mAttractForce = 0.0f;
+ }
+ }
+
+ void Draw(int pass)
+ {
+ if (!g_drawMesh)
+ return;
+
+ for (int b = 0; b < int(mBatches.size()); ++b)
+ {
+ if (mBatches[b].mInstanceTransforms.size())
+ {
+ extern Colour gColors[];
+ DrawGpuMeshInstances(mBatches[b].mMesh, &mBatches[b].mInstanceTransforms[0], mBatches[b].mInstanceTransforms.size(), Vec3(gColors[b % 8]));
+ }
+ }
+ }
+
+ float mAttractForce;
+
+ std::vector<MeshBatch> mBatches;
+ int numActive;
+
+ int mGroupCounter;
+ std::vector<Instance> mInstances;
+};
diff --git a/demo/scenes/deformables.h b/demo/scenes/deformables.h
new file mode 100644
index 0000000..2de87d7
--- /dev/null
+++ b/demo/scenes/deformables.h
@@ -0,0 +1,42 @@
+
+
+class Deformables : public Scene
+{
+public:
+
+ Deformables(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ g_params.dynamicFriction = 0.25f;
+
+ for (int i=0; i < 5; i++)
+ AddRandomConvex(10, Vec3(i*2.0f, 0.0f, Randf(0.0f, 2.0f)), 0.5f, 1.0f, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi));
+
+ if (0)
+ {
+ int group = 0;
+
+ float minSize = 0.2f;
+ float maxSize = 0.4f;
+
+ for (int z=0; z < 1; ++z)
+ for (int y=0; y < 1; ++y)
+ for (int x=0; x < 5; ++x)
+ CreateRandomBody(12, Vec3(2.0f*x, 2.0f + y, 1.0f + 1.0f*z), minSize, maxSize, RandomUnitVector(), Randf(0.0f, k2Pi), 1.0f, NvFlexMakePhase(group++, 0), 0.25f);
+ }
+ else
+ {
+ CreateTetMesh(GetFilePathByPlatform("../../data/tets/duck.tet").c_str(), Vec3(2.0f, 1.0f, 2.0f), 2.00000105f, 1.0f, 0);
+ CreateTetMesh(GetFilePathByPlatform("../../data/tets/duck.tet").c_str(), Vec3(2.0f, 3.0f, 2.0f), 2.00000105f, 1.0f, 1);
+ }
+
+ g_params.numIterations = 5;
+ g_params.relaxationFactor = 1.0f;
+ g_params.radius = 0.025f;
+
+ // draw options
+ g_drawPoints = true;
+ g_drawSprings = false;
+ }
+};
diff --git a/demo/scenes/envcloth.h b/demo/scenes/envcloth.h
new file mode 100644
index 0000000..7405739
--- /dev/null
+++ b/demo/scenes/envcloth.h
@@ -0,0 +1,77 @@
+
+
+class EnvironmentalCloth: public Scene
+{
+public:
+
+ EnvironmentalCloth(const char* name, int dimx, int dimz, int gridx, int gridz) :
+ Scene(name),
+ mDimX(dimx),
+ mDimZ(dimz),
+ mGridX(gridx),
+ mGridZ(gridz) {}
+
+ virtual void Initialize()
+ {
+ float scale = 1.0f;
+
+ float minSize = 0.5f*scale;
+ float maxSize = 1.0f*scale;
+
+ for (int i=0; i < 5; i++)
+ AddRandomConvex(10, Vec3(i*2.0f, 0.0f, Randf(0.0f, 2.0f)), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi));
+
+ float stretchStiffness = 0.9f;
+ float bendStiffness = 0.8f;
+ float shearStiffness = 0.5f;
+
+ int dimx = mDimX;
+ int dimz = mDimZ;
+ float radius = 0.05f*scale;
+ g_params.gravity[1] *= scale;
+
+ int gridx = mGridX;
+ int gridz = mGridZ;
+
+ int clothIndex = 0;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide);
+
+ for (int x=0; x < gridx; ++x)
+ {
+ for (int y=0; y < 1; ++y)
+ {
+ for (int z=0; z < gridz; ++z)
+ {
+ clothIndex++;
+
+ CreateSpringGrid(Vec3(x*dimx*radius, scale*(1.0f + z*0.5f), z*dimx*radius), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, Vec3(Randf(-0.2f, 0.2f)), 1.0f);
+ }
+ }
+ }
+
+ g_params.radius = radius*1.05f;
+ g_params.dynamicFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.1f;
+ g_params.lift = 0.5f;
+ g_params.collisionDistance = 0.05f;
+
+ // cloth converges faster with a global relaxation factor
+ g_params.relaxationMode = eNvFlexRelaxationGlobal;
+ g_params.relaxationFactor = 0.25f;
+
+ g_windStrength = 0.0f;
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = false;
+ }
+
+ int mDimX;
+ int mDimZ;
+ int mGridX;
+ int mGridZ;
+};
diff --git a/demo/scenes/flag.h b/demo/scenes/flag.h
new file mode 100644
index 0000000..c8bf3ff
--- /dev/null
+++ b/demo/scenes/flag.h
@@ -0,0 +1,83 @@
+
+class FlagCloth: public Scene
+{
+public:
+
+ FlagCloth(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ int dimx = 64;
+ int dimz = 32;
+ float radius = 0.05f;
+
+ float stretchStiffness = 0.9f;
+ float bendStiffness = 1.0f;
+ float shearStiffness = 0.9f;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide);
+
+ CreateSpringGrid(Vec3(0.0f, 0.0f, -3.0f), dimx, dimz, 1, radius, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+
+ const int c1 = 0;
+ const int c2 = dimx*(dimz-1);
+
+ g_buffers->positions[c1].w = 0.0f;
+ g_buffers->positions[c2].w = 0.0f;
+
+ // add tethers
+ for (int i=0; i < int(g_buffers->positions.size()); ++i)
+ {
+ // hack to rotate cloth
+ swap(g_buffers->positions[i].y, g_buffers->positions[i].z);
+ g_buffers->positions[i].y *= -1.0f;
+
+ g_buffers->velocities[i] = RandomUnitVector()*0.1f;
+
+ float minSqrDist = FLT_MAX;
+
+ if (i != c1 && i != c2)
+ {
+ float stiffness = -0.8f;
+ float give = 0.1f;
+
+ float sqrDist = LengthSq(Vec3(g_buffers->positions[c1])-Vec3(g_buffers->positions[c2]));
+
+ if (sqrDist < minSqrDist)
+ {
+ CreateSpring(c1, i, stiffness, give);
+ CreateSpring(c2, i, stiffness, give);
+
+ minSqrDist = sqrDist;
+ }
+ }
+ }
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 4;
+ g_params.drag = 0.06f;
+ g_params.relaxationFactor = 1.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = false;
+ g_windFrequency *= 2.0f;
+ g_windStrength = 10.0f;
+
+ }
+
+ void Update()
+ {
+ const Vec3 kWindDir = Vec3(3.0f, 15.0f, 0.0f);
+ const float kNoise = fabsf(Perlin1D(g_windTime*0.05f, 2, 0.25f));
+ Vec3 wind = g_windStrength*kWindDir*Vec3(kNoise, kNoise*0.1f, -kNoise*0.1f);
+
+ g_params.wind[0] = wind.x;
+ g_params.wind[1] = wind.y;
+ g_params.wind[2] = wind.z;
+ }
+};
+
diff --git a/demo/scenes/fluidblock.h b/demo/scenes/fluidblock.h
new file mode 100644
index 0000000..82de2cb
--- /dev/null
+++ b/demo/scenes/fluidblock.h
@@ -0,0 +1,72 @@
+
+
+class FluidBlock : public Scene
+{
+public:
+
+ FluidBlock(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float minSize = 0.5f;
+ float maxSize = 0.7f;
+
+ float radius = 0.1f;
+ float restDistance = radius*0.55f;
+ int group = 0;
+
+ AddRandomConvex(6, Vec3(5.0f, -0.1f, 0.6f), 1.0f, 1.0f, Vec3(1.0f, 1.0f, 0.0f), 0.0f);
+
+ float ly = 0.5f;
+
+ AddRandomConvex(10, Vec3(2.5f, ly*0.5f, 1.f), minSize*0.5f, maxSize*0.5f, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, 2.0f*kPi));
+
+ AddRandomConvex(12, Vec3(3.8f, ly-0.5f, 1.f), minSize, maxSize, Vec3(1.0f, 0.0f, 0.0f), Randf(0.0f, 2.0f*kPi));
+ AddRandomConvex(12, Vec3(3.8f, ly-0.5f, 2.6f), minSize, maxSize, Vec3(1.0f, 0.0f, 0.0f), 0.2f + Randf(0.0f, 2.0f*kPi));
+
+ AddRandomConvex(12, Vec3(4.6f, ly, 0.2f), minSize, maxSize, Vec3(1.0f, 0.0f, 1.0f), Randf(0.0f, 2.0f*kPi));
+ AddRandomConvex(12, Vec3(4.6f, ly, 2.0f), minSize, maxSize, Vec3(1.0f, 0.0f, 1.0f), 0.2f + Randf(0.0f, 2.0f*kPi));
+
+ float size = 0.3f;
+ for (int i=0; i < 32; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/torus.obj").c_str(), Vec3(4.5f, 2.0f + radius*2.0f*i, 1.0f), size, 0.0f, radius*0.5f, Vec3(0.0f, 0.0f, 0.0f), 0.125f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+
+ g_numSolidParticles = g_buffers->positions.size();
+
+ float sizex = 1.76f;
+ float sizey = 2.20f;
+ float sizez = 3.50f;
+
+ int x = int(sizex/restDistance);
+ int y = int(sizey/restDistance);
+ int z = int(sizez/restDistance);
+
+ CreateParticleGrid(Vec3(0.0f, restDistance*0.5f, 0.0f), x, y, z, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid));
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.0f;
+ g_params.fluid = true;
+ g_params.viscosity = 0.0f;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 40.f;
+ g_params.anisotropyScale = 20.0f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.numPlanes = 5;
+ //g_params.cohesion = 0.05f;
+
+ g_maxDiffuseParticles = 128*1024;
+ g_diffuseScale = 0.75f;
+
+ g_waveFloorTilt = -0.025f;
+
+ g_lightDistance *= 0.5f;
+
+ // draw options
+ g_drawDensity = true;
+ g_drawDiffuse = true;
+ g_drawEllipsoids = true;
+ g_drawPoints = false;
+
+ g_warmup = true;
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/fluidclothcoupling.h b/demo/scenes/fluidclothcoupling.h
new file mode 100644
index 0000000..403a775
--- /dev/null
+++ b/demo/scenes/fluidclothcoupling.h
@@ -0,0 +1,192 @@
+
+class FluidClothCoupling : public Scene
+{
+public:
+
+ FluidClothCoupling(const char* name, bool viscous) : Scene(name), mViscous(viscous) {}
+
+ void Initialize()
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.4f;
+ float shearStiffness = 0.4f;
+
+ int dimx = 32;
+ int dimy = 32;
+ float radius = 0.1f;
+ float invmass = 0.25f;
+ int group = 0;
+
+ {
+ int clothStart = 0;
+
+ CreateSpringGrid(Vec3(0.0f, 1.0f, 0.0f), dimx, dimy, 1, radius*0.25f, NvFlexMakePhase(group++, 0), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), invmass);
+
+ int corner0 = clothStart + 0;
+ int corner1 = clothStart + dimx-1;
+ int corner2 = clothStart + dimx*(dimy-1);
+ int corner3 = clothStart + dimx*dimy-1;
+
+ g_buffers->positions[corner0].w = 0.0f;
+ g_buffers->positions[corner1].w = 0.0f;
+ g_buffers->positions[corner2].w = 0.0f;
+ g_buffers->positions[corner3].w = 0.0f;
+
+ // add tethers
+ for (int i=clothStart; i < int(g_buffers->positions.size()); ++i)
+ {
+ float x = g_buffers->positions[i].x;
+ g_buffers->positions[i].y = 1.5f - sinf(DegToRad(25.0f))*x;
+ g_buffers->positions[i].x = cosf(DegToRad(25.0f))*x;
+
+ //g_buffers->positions[i].y += 0.5f-g_buffers->positions[i].x;
+
+ if (i != corner0 && i != corner1 && i != corner2 && i != corner3)
+ {
+ float stiffness = -0.5f;
+ float give = 0.05f;
+
+ CreateSpring(corner0, i, stiffness, give);
+ CreateSpring(corner1, i, stiffness, give);
+ CreateSpring(corner2, i, stiffness, give);
+ CreateSpring(corner3, i, stiffness, give);
+ }
+ }
+
+ g_buffers->positions[corner1] = g_buffers->positions[corner0] + (g_buffers->positions[corner1]-g_buffers->positions[corner0])*0.9f;
+ g_buffers->positions[corner2] = g_buffers->positions[corner0] + (g_buffers->positions[corner2]-g_buffers->positions[corner0])*0.9f;
+ g_buffers->positions[corner3] = g_buffers->positions[corner0] + (g_buffers->positions[corner3]-g_buffers->positions[corner0])*0.9f;
+ }
+
+ {
+ // net
+ int clothStart = g_buffers->positions.size();
+
+ CreateSpringGrid(Vec3(0.75f, 1.0f, 0.0f), dimx, dimy, 1, radius*0.25f, NvFlexMakePhase(group++, 0), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), invmass);
+
+ int corner0 = clothStart + 0;
+ int corner1 = clothStart + dimx-1;
+ int corner2 = clothStart + dimx*(dimy-1);
+ int corner3 = clothStart + dimx*dimy-1;
+
+ g_buffers->positions[corner0].w = 0.0f;
+ g_buffers->positions[corner1].w = 0.0f;
+ g_buffers->positions[corner2].w = 0.0f;
+ g_buffers->positions[corner3].w = 0.0f;
+
+ // add tethers
+ for (int i=clothStart; i < int(g_buffers->positions.size()); ++i)
+ {
+ if (i != corner0 && i != corner1 && i != corner2 && i != corner3)
+ {
+ float stiffness = -0.5f;
+ float give = 0.1f;
+
+ CreateSpring(corner0, i, stiffness, give);
+ CreateSpring(corner1, i, stiffness, give);
+ CreateSpring(corner2, i, stiffness, give);
+ CreateSpring(corner3, i, stiffness, give);
+ }
+ }
+
+ g_buffers->positions[corner1] = g_buffers->positions[corner0] + (g_buffers->positions[corner1]-g_buffers->positions[corner0])*0.8f;
+ g_buffers->positions[corner2] = g_buffers->positions[corner0] + (g_buffers->positions[corner2]-g_buffers->positions[corner0])*0.8f;
+ g_buffers->positions[corner3] = g_buffers->positions[corner0] + (g_buffers->positions[corner3]-g_buffers->positions[corner0])*0.8f;
+
+ }
+
+ {
+ // net
+ int clothStart = g_buffers->positions.size();
+
+ CreateSpringGrid(Vec3(1.5f, 0.5f, 0.0f), dimx, dimy, 1, radius*0.25f, NvFlexMakePhase(group++, 0), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), invmass);
+
+ int corner0 = clothStart + 0;
+ int corner1 = clothStart + dimx-1;
+ int corner2 = clothStart + dimx*(dimy-1);
+ int corner3 = clothStart + dimx*dimy-1;
+
+ g_buffers->positions[corner0].w = 0.0f;
+ g_buffers->positions[corner1].w = 0.0f;
+ g_buffers->positions[corner2].w = 0.0f;
+ g_buffers->positions[corner3].w = 0.0f;
+
+ // add tethers
+ for (int i=clothStart; i < int(g_buffers->positions.size()); ++i)
+ {
+ if (i != corner0 && i != corner1 && i != corner2 && i != corner3)
+ {
+ float stiffness = -0.5f;
+ float give = 0.1f;
+
+ CreateSpring(corner0, i, stiffness, give);
+ CreateSpring(corner1, i, stiffness, give);
+ CreateSpring(corner2, i, stiffness, give);
+ CreateSpring(corner3, i, stiffness, give);
+ }
+ }
+
+ g_buffers->positions[corner1] = g_buffers->positions[corner0] + (g_buffers->positions[corner1]-g_buffers->positions[corner0])*0.8f;
+ g_buffers->positions[corner2] = g_buffers->positions[corner0] + (g_buffers->positions[corner2]-g_buffers->positions[corner0])*0.8f;
+ g_buffers->positions[corner3] = g_buffers->positions[corner0] + (g_buffers->positions[corner3]-g_buffers->positions[corner0])*0.8f;
+
+ }
+
+ g_numSolidParticles = g_buffers->positions.size();
+ g_ior = 1.0f;
+
+ g_numExtraParticles = 64*1024;
+
+ g_params.radius = radius;
+ g_params.fluid = true;
+ g_params.numIterations = 5;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.anisotropyScale = 30.0f;
+ g_params.fluidRestDistance = g_params.radius*0.5f;
+ g_params.smoothing = 0.5f;
+ g_params.solidPressure = 0.25f;
+ g_numSubsteps = 3;
+ //g_params.numIterations = 6;
+
+ g_params.maxSpeed = 0.5f*g_numSubsteps*g_params.radius/g_dt;
+
+ g_maxDiffuseParticles = 32*1024;
+ g_diffuseScale = 0.5f;
+ g_lightDistance = 3.0f;
+
+ // for viscous goo
+ if (mViscous)
+ {
+ g_fluidColor = Vec4(0.0f, 0.8f, 0.2f, 1.0f);
+
+ g_params.dynamicFriction = 0.3f;
+ g_params.cohesion = 0.025f;
+ g_params.viscosity = 50.85f;
+ }
+ else
+ {
+ g_params.dynamicFriction = 0.125f;
+ g_params.viscosity = 0.1f;
+ g_params.cohesion = 0.0035f;
+ g_params.viscosity = 4.0f;
+ }
+
+ g_emitters[0].mEnabled = false;
+
+ Emitter e;
+ e.mDir = Normalize(Vec3(1.0f, 0.0f, 0.0f));
+ e.mEnabled = true;
+ e.mPos = Vec3(-0.25f, 1.75f, 0.5f);
+ e.mRight = Cross(e.mDir, Vec3(0.0f, 0.0f, 1.0f));
+ e.mSpeed = (g_params.fluidRestDistance/(g_dt*2.0f));
+
+ g_emitters.push_back(e);
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = false;
+ g_drawEllipsoids = true;
+ }
+
+ bool mViscous;
+}; \ No newline at end of file
diff --git a/demo/scenes/forcefield.h b/demo/scenes/forcefield.h
new file mode 100644
index 0000000..1ffcd12
--- /dev/null
+++ b/demo/scenes/forcefield.h
@@ -0,0 +1,92 @@
+
+
+class ForceField : public Scene
+{
+public:
+
+
+ ForceField(const char* name) : Scene(name)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ const float radius = 0.01f;
+ const int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide);
+
+ CreateParticleGrid(Vec3(-1.0f, radius, 0.0f), 200, 6, 50, radius*0.5f, Vec3(0.0f), 1.0f, false, 0.0f, phase);
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.4f;
+ g_params.staticFriction = 0.4f;
+ g_params.particleFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 2;
+ g_params.viscosity = 0.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = true;
+
+ callback = NULL;
+
+ }
+
+ virtual void PostInitialize()
+ {
+ // free previous callback, todo: destruction phase for tests
+ if (callback)
+ NvFlexExtDestroyForceFieldCallback(callback);
+
+ // create new callback
+ callback = NvFlexExtCreateForceFieldCallback(g_flex);
+
+ // expand scene bounds to include force field
+ g_sceneLower -= Vec3(1.0f);
+ g_sceneUpper += Vec3(1.0f);
+ }
+
+ void DrawCircle(const Vec3& pos, const Vec3& u, const Vec3& v, float radius, int segments)
+ {
+ BeginLines();
+
+ Vec3 start = pos + radius*v;
+
+ for (int i=1; i <=segments; ++i)
+ {
+ float theta = k2Pi*(float(i)/segments);
+ Vec3 end = pos + radius*sinf(theta)*u + radius*cosf(theta)*v;
+
+ DrawLine(start, end, Vec4(1.0f));
+
+ start = end;
+ }
+
+ EndLines();
+ }
+
+ virtual void Draw(int phase)
+ {
+ DrawCircle(forcefield.mPosition, Vec3(1.0f, 0.0f, 0.0f), Vec3(0.0f, 1.0f, 0.0f), forcefield.mRadius, 20);
+ DrawCircle(forcefield.mPosition, Vec3(0.0f, 0.0f, 1.0f), Vec3(0.0f, 1.0f, 0.0f), forcefield.mRadius, 20);
+ }
+
+ virtual void Update()
+ {
+ float time = g_frame*g_dt;
+
+ (Vec3&)forcefield.mPosition = Vec3((sinf(time)), 0.5f, 0.0f);
+ forcefield.mRadius = (sinf(time*1.5f)*0.5f + 0.5f);
+ forcefield.mStrength = -30.0f;
+ forcefield.mMode = eNvFlexExtModeForce;
+ forcefield.mLinearFalloff = true;
+
+ NvFlexExtSetForceFields(callback, &forcefield, 1);
+ }
+
+ NvFlexExtForceField forcefield;
+
+ NvFlexExtForceFieldCallback* callback;
+
+}; \ No newline at end of file
diff --git a/demo/scenes/frictionmoving.h b/demo/scenes/frictionmoving.h
new file mode 100644
index 0000000..ae8ba8e
--- /dev/null
+++ b/demo/scenes/frictionmoving.h
@@ -0,0 +1,93 @@
+
+class FrictionMovingShape: public Scene
+{
+public:
+
+ FrictionMovingShape(const char* name, int type) :
+ Scene(name), mType(type) {}
+
+ virtual void Initialize()
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.5f;
+ float shearStiffness = 0.5f;
+
+ float radius = 0.05f;
+
+ int dimx = 40;
+ int dimz = 40;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+
+ float spacing = radius*0.8f;
+
+ for (int i=0; i < 3; ++i)
+ CreateSpringGrid(Vec3(-dimx*spacing*0.5f, 1.5f + i*0.2f, -dimz*spacing*0.5f), dimx, dimz, 1, spacing, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.45f;
+ g_params.particleFriction = 0.45f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.05f;
+ g_params.collisionDistance = radius*0.5f;
+ g_params.relaxationMode = eNvFlexRelaxationGlobal;
+ g_params.relaxationFactor = 0.25f;
+ g_params.numPlanes = 1;
+
+ g_numSubsteps = 2;
+
+ g_windStrength = 0.0f;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = false;
+
+ g_lightDistance *= 1.5f;
+
+ mTime = 0.0f;
+ }
+
+ void Update()
+ {
+ ClearShapes();
+
+ mTime += g_dt;
+
+ // let cloth settle on object
+ float startTime = 1.0f;
+
+ float time = Max(0.0f, mTime-startTime);
+ float lastTime = Max(0.0f, time-g_dt);
+
+ const float rotationSpeed = 1.0f;
+ const float translationSpeed = 1.0f;
+
+ Vec3 pos = Vec3(translationSpeed*(1.0f-cosf(time)), 0.5f, 0.0f);
+ Vec3 prevPos = Vec3(translationSpeed*(1.0f-cosf(lastTime)), 0.5f, 0.0f);
+
+ Quat rot = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), 1.0f-cosf(rotationSpeed*time));
+ Quat prevRot = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), 1.0f-cosf(rotationSpeed*lastTime));
+
+ switch(mType)
+ {
+ case 0:
+ AddBox(Vec3(1.0f, 0.5f, 1.0f), pos, rot);
+ break;
+ case 1:
+ AddSphere(0.5f, pos, rot);
+ break;
+ case 2:
+ AddCapsule(0.1f, 1.5f, pos, rot);
+ break;
+ };
+
+ g_buffers->shapePrevPositions[0] = Vec4(prevPos, 0.0f);
+ g_buffers->shapePrevRotations[0] = prevRot;
+
+ UpdateShapes();
+ }
+
+ float mTime;
+ int mType;
+}; \ No newline at end of file
diff --git a/demo/scenes/frictionramp.h b/demo/scenes/frictionramp.h
new file mode 100644
index 0000000..f9f36cf
--- /dev/null
+++ b/demo/scenes/frictionramp.h
@@ -0,0 +1,40 @@
+
+
+class FrictionRamp : public Scene
+{
+public:
+
+ FrictionRamp(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.1f;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.35f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.0f;
+ g_params.lift = 0.0f;
+ g_params.collisionDistance = radius*0.5f;
+
+ g_windStrength = 0.0f;
+
+ g_numSubsteps = 1;
+
+ // draw options
+ g_drawPoints = false;
+ g_wireframe = false;
+ g_drawSprings = false;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ // box
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), Vec3(0.0f, 3.5f, -i*2.0f), 0.5f, 0.0f, radius, 0.0f, 1.0f, true, 1.0f, NvFlexMakePhase(i, 0), true, 0.0f);
+
+ // ramp
+ AddBox(Vec3(5.0f, 0.25f, 1.f), Vec3(3.0f, 1.0f, -i*2.0f), QuatFromAxisAngle(Vec3(0.0f, 0.0f, 1.0f), DegToRad(-11.25f*(i + 1))));
+ }
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/gamemesh.h b/demo/scenes/gamemesh.h
new file mode 100644
index 0000000..6de6bd0
--- /dev/null
+++ b/demo/scenes/gamemesh.h
@@ -0,0 +1,191 @@
+
+class GameMesh : public Scene
+{
+public:
+
+ GameMesh(const char* name, int scene) : Scene(name), mScene(scene) {}
+
+ void Initialize()
+ {
+ Mesh* level = ImportMeshFromBin(GetFilePathByPlatform("../../data/testzone.bin").c_str());
+ level->Normalize(100.0f);
+ level->Transform(TranslationMatrix(Point3(0.0f, -5.0f, 0.0f)));
+ level->CalculateNormals();
+
+ Vec3 lower, upper;
+ level->GetBounds(lower, upper);
+ Vec3 center = (lower+upper)*0.5f;
+
+ NvFlexTriangleMeshId mesh = CreateTriangleMesh(level);
+ AddTriangleMesh(mesh, Vec3(), Quat(), 1.0f);
+
+ delete level;
+
+ int group = 0;
+
+ // rigids
+ if (mScene == 0)
+ {
+ float radius = 0.05f;
+
+ for (int z=0; z < 80; ++z)
+ for (int x=0; x < 80; ++x)
+ CreateParticleGrid(
+ center - Vec3(-16.0f, 0.0f, 15.0f) + 2.0f*Vec3(x*radius*2 - 1.0f, 1.0f + radius, 1.f + z*2.0f*radius) + Vec3(Randf(radius), 0.0f, Randf(radius)),
+ 2, 2 + int(Randf(0.0f, 4.0f)), 2, radius*0.9f, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), 0.0f);
+
+ // separte solid particle count
+ g_numSolidParticles = g_buffers->positions.size();
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.3f;
+ g_params.dissipation = 0.0f;
+ g_params.fluid = false;
+ g_params.fluidRestDistance = g_params.radius*0.5f;
+ g_params.viscosity = 0.05f;
+ g_params.anisotropyScale = 20.0f;
+ g_params.numIterations = 2;
+ g_params.numPlanes = 1;
+ g_params.sleepThreshold = g_params.radius*0.3f;
+ g_params.maxSpeed = g_numSubsteps*g_params.radius/g_dt;
+ g_params.collisionDistance = radius*0.5f;
+ g_params.shapeCollisionMargin = g_params.collisionDistance*0.05f;
+
+ g_numSubsteps = 2;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = 2.0f*(g_params.radius*0.5f)/g_dt;
+
+ // draw options
+ g_drawPoints = true;
+ g_drawMesh = false;
+ }
+
+ // basic particles
+ if (mScene == 1)
+ {
+ float radius = 0.1f;
+
+ CreateParticleGrid(center - Vec3(2.0f, 7.0f, 2.0f) , 32, 64, 32, radius*1.02f, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), 0.0f);
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.1f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 4;
+ g_params.numPlanes = 1;
+ g_params.fluid = false;
+ g_params.particleCollisionMargin = g_params.radius*0.1f;
+ g_params.restitution = 0.0f;
+
+ g_params.collisionDistance = g_params.radius*0.5f;
+ g_params.shapeCollisionMargin = g_params.collisionDistance*0.05f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = true;
+ }
+
+ // fluid particles
+ if (mScene == 2)
+ {
+ float radius = 0.1f;
+ float restDistance = radius*0.6f;
+
+ CreateParticleGrid(center - Vec3(0.0f, 7.0f, 2.0f) , 32, 64, 32, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), 0.0f);
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.1f;
+ g_params.dissipation = 0.0f;
+ g_params.numPlanes = 1;
+ g_params.fluidRestDistance = restDistance;
+ g_params.viscosity = 0.5f;
+ g_params.numIterations = 3;
+ g_params.anisotropyScale = 30.0f;
+ g_params.smoothing = 0.5f;
+ g_params.fluid = true;
+ g_params.relaxationFactor = 1.0f;
+ g_params.restitution = 0.0f;
+ g_params.smoothing = 0.5f;
+ g_params.collisionDistance = g_params.radius*0.5f;
+ g_params.shapeCollisionMargin = g_params.collisionDistance*0.05f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ g_drawDiffuse = true;
+
+ g_lightDistance = 5.0f;
+ }
+
+ // cloth
+ if (mScene == 3)
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.8f;
+ float shearStiffness = 0.8f;
+
+ int dimx = 32;
+ int dimz = 32;
+ float radius = 0.05f;
+
+ int gridx = 8;
+ int gridz = 3;
+
+ for (int x=0; x < gridx; ++x)
+ {
+ for (int y=0; y < 1; ++y)
+ {
+ for (int z=0; z < gridz; ++z)
+ {
+ CreateSpringGrid(center - Vec3(9.0f, 1.0f, 0.1f) + Vec3(x*dimx*radius, 0.0f, z*1.0f), dimx, dimz, 1, radius*0.95f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter), stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+ }
+ }
+ }
+
+ Vec3 l, u;
+ GetParticleBounds(l, u);
+
+ Vec3 center = (u+l)*0.5f;
+ printf("%f %f %f\n", center.x, center.y, center.z);
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.4f;
+ g_params.staticFriction = 0.5f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.drag = 0.06f;
+ g_params.sleepThreshold = g_params.radius*0.125f;
+ g_params.relaxationFactor = 2.0f;
+ g_params.collisionDistance = g_params.radius;
+ g_params.shapeCollisionMargin = g_params.collisionDistance*0.05f;
+
+ g_windStrength = 0.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ }
+ }
+
+ virtual void PostInitialize()
+ {
+ // just focus on the particles, don't need to see the whole level
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ g_sceneUpper = upper;
+ g_sceneLower = lower;
+ }
+
+ virtual void CenterCamera(void)
+ {
+ g_camPos = Vec3((g_sceneLower.x+g_sceneUpper.x)*0.5f, min(g_sceneUpper.y*1.25f, 6.0f), g_sceneUpper.z + min(g_sceneUpper.y, 6.0f)*2.0f);
+ g_camAngle = Vec3(0.0f, -DegToRad(15.0f), 0.0f);
+ }
+
+ int mScene;
+};
diff --git a/demo/scenes/googun.h b/demo/scenes/googun.h
new file mode 100644
index 0000000..79ff396
--- /dev/null
+++ b/demo/scenes/googun.h
@@ -0,0 +1,72 @@
+
+class GooGun : public Scene
+{
+public:
+
+ GooGun(const char* name, bool viscous) : Scene(name), mViscous(viscous) {}
+
+ virtual void Initialize()
+ {
+ float minSize = 0.5f;
+ float maxSize = 1.0f;
+
+ float radius = 0.1f;
+
+ for (int i = 0; i < 5; i++)
+ AddRandomConvex(10, Vec3(i*2.0f, 0.0f, Randf(0.0f, 2.0f)), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi*10.0f));
+
+ g_params.radius = radius;
+
+ g_params.fluid = true;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.fluidRestDistance = g_params.radius*0.55f;
+ g_params.anisotropyScale = 2.0f / radius;
+ g_params.smoothing = 0.5f;
+ g_params.relaxationFactor = 1.f;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = 0.01f;
+ g_params.shapeCollisionMargin = g_params.collisionDistance*0.25f;
+
+ if (mViscous)
+ {
+ g_fluidColor = Vec4(0.0f, 0.8f, 0.2f, 1.0f);
+
+ g_params.dynamicFriction = 1.0f;
+ g_params.viscosity = 50.0f;
+ g_params.adhesion = 0.5f;
+ g_params.cohesion = 0.3f;
+ g_params.surfaceTension = 0.0f;
+ }
+ else
+ {
+ g_params.dynamicFriction = 0.25f;
+ g_params.viscosity = 0.5f;
+ g_params.cohesion = 0.05f;
+ g_params.adhesion = 0.0f;
+ }
+
+ g_numExtraParticles = 64 * 1024;
+
+#if 1
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/armadillo.ply").c_str(), 128);
+ AddSDF(sdf, Vec3(2.0f, 0.0f, -1.0f), Quat(), 2.0f);
+#else
+ // Test multiple SDFs
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/armadillo.ply").c_str(), 128);
+ AddSDF(sdf, Vec3(2.0f, 0.0f, 0.0f), Quat(), 2.0f);
+
+ NvFlexDistanceFieldId sdf2 = CreateSDF(GetFilePathByPlatform("../../data/bunny.ply").c_str(), 128);
+ AddSDF(sdf2, Vec3(4.0f, 0.0f, 0.0f), Quat(), 2.0f);
+#endif
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.fluidRestDistance*2.f / g_dt);
+
+ // draw options
+ g_drawEllipsoids = true;
+ g_pause = false;
+ }
+
+ bool mViscous;
+};
diff --git a/demo/scenes/granularpile.h b/demo/scenes/granularpile.h
new file mode 100644
index 0000000..56a85ea
--- /dev/null
+++ b/demo/scenes/granularpile.h
@@ -0,0 +1,57 @@
+
+
+class GranularPile : public Scene
+{
+public:
+
+ GranularPile(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ // granular pile
+ float radius = 0.075f;
+
+ Vec3 lower(8.0f, 4.0f, 2.0f);
+
+ CreateParticleShape(GetFilePathByPlatform("../../data/sphere.ply").c_str(), lower, 1.0f, 0.0f, radius, 0.0f, 0.f, true, 1.0f, NvFlexMakePhase(1, 0), true, 0.00f);
+ g_numSolidParticles = g_buffers->positions.size();
+
+ CreateParticleShape(GetFilePathByPlatform("../../data/sandcastle.obj").c_str(), Vec3(-2.0f, -radius*0.15f, 0.0f), 4.0f, 0.0f, radius*1.0001f, 0.0f, 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), false, 0.00f);
+
+ g_numSubsteps = 2;
+
+ g_params.radius = radius;
+ g_params.staticFriction = 1.0f;
+ g_params.dynamicFriction = 0.5f;
+ g_params.viscosity = 0.0f;
+ g_params.numIterations = 12;
+ g_params.particleCollisionMargin = g_params.radius*0.25f; // 5% collision margin
+ g_params.sleepThreshold = g_params.radius*0.25f;
+ g_params.shockPropagation = 6.f;
+ g_params.restitution = 0.2f;
+ g_params.relaxationFactor = 1.f;
+ g_params.damping = 0.14f;
+ g_params.numPlanes = 1;
+
+ // draw options
+ g_drawPoints = true;
+ g_warmup = false;
+
+ // hack, change the color of phase 0 particles to 'sand'
+ extern Colour gColors[];
+ gColors[0] = Colour(0.805f, 0.702f, 0.401f);
+ }
+
+ void Update()
+ {
+ // launch ball after 3 seconds
+ if (g_frame == 180)
+ {
+ for (int i=0; i < g_numSolidParticles; ++i)
+ {
+ g_buffers->positions[i].w = 0.9f;
+ g_buffers->velocities[i] = Vec3(-15.0f, 0.0f, 0.0f);
+ }
+ }
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/granularshape.h b/demo/scenes/granularshape.h
new file mode 100644
index 0000000..6d21738
--- /dev/null
+++ b/demo/scenes/granularshape.h
@@ -0,0 +1,36 @@
+
+
+
+class GranularShape : public Scene
+{
+public:
+
+ GranularShape(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ // granular dragon
+ CreateParticleShape(GetFilePathByPlatform("../../data/dragon.obj").c_str(),Vec3(0.0f, 2.5f, 0.0f), 16.0f, DegToRad(-20.0f), g_params.radius*1.05f, Vec3(0.0f, 0.0f, 0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), false, g_params.radius*0.05f);
+
+ AddBox(Vec3(8.0f, 8.0f, 5.0f));
+ g_buffers->shapePositions[0] += Vec4(0.0f, -1.5f, 0.0f, 0.0f);
+
+ g_params.staticFriction = 1.0f;
+ g_params.dynamicFriction = 0.65f;
+ g_params.dissipation = 0.01f;
+ g_params.numIterations = 6;
+ g_params.particleCollisionMargin = g_params.radius*0.5f; // 5% collision margin
+ g_params.sleepThreshold = g_params.radius*0.35f;
+ g_params.shockPropagation = 3.f;
+ g_params.restitution = 0.01f;
+ g_params.gravity[1] *= 1.f;
+
+ g_numSubsteps = 3;
+
+ extern Colour gColors[];
+ gColors[1] = Colour(0.805f, 0.702f, 0.401f);
+
+ // draw options
+ g_drawPoints = true;
+ }
+};
diff --git a/demo/scenes/inflatable.h b/demo/scenes/inflatable.h
new file mode 100644
index 0000000..58f5f08
--- /dev/null
+++ b/demo/scenes/inflatable.h
@@ -0,0 +1,159 @@
+
+
+class Inflatable : public Scene
+{
+public:
+
+ Inflatable(const char* name) : Scene(name) {}
+
+ virtual ~Inflatable()
+ {
+ for (size_t i = 0; i < mCloths.size(); ++i)
+ delete mCloths[i];
+ }
+
+ void AddInflatable(const Mesh* mesh, float overPressure, int phase)
+ {
+ const int startVertex = g_buffers->positions.size();
+
+ // add mesh to system
+ for (size_t i = 0; i < mesh->GetNumVertices(); ++i)
+ {
+ const Vec3 p = Vec3(mesh->m_positions[i]);
+
+ g_buffers->positions.push_back(Vec4(p.x, p.y, p.z, 1.0f));
+ g_buffers->velocities.push_back(0.0f);
+ g_buffers->phases.push_back(phase);
+ }
+
+ int triOffset = g_buffers->triangles.size();
+ int triCount = mesh->GetNumFaces();
+
+ g_buffers->inflatableTriOffsets.push_back(triOffset / 3);
+ g_buffers->inflatableTriCounts.push_back(mesh->GetNumFaces());
+ g_buffers->inflatablePressures.push_back(overPressure);
+
+ for (size_t i = 0; i < mesh->m_indices.size(); i += 3)
+ {
+ int a = mesh->m_indices[i + 0];
+ int b = mesh->m_indices[i + 1];
+ int c = mesh->m_indices[i + 2];
+
+ Vec3 n = -Normalize(Cross(mesh->m_positions[b] - mesh->m_positions[a], mesh->m_positions[c] - mesh->m_positions[a]));
+ g_buffers->triangleNormals.push_back(n);
+
+ g_buffers->triangles.push_back(a + startVertex);
+ g_buffers->triangles.push_back(b + startVertex);
+ g_buffers->triangles.push_back(c + startVertex);
+ }
+
+ // create a cloth mesh using the global positions / indices
+ ClothMesh* cloth = new ClothMesh(&g_buffers->positions[0], g_buffers->positions.size(), &g_buffers->triangles[triOffset], triCount * 3, 0.8f, 1.0f);
+
+ for (size_t i = 0; i < cloth->mConstraintIndices.size(); ++i)
+ g_buffers->springIndices.push_back(cloth->mConstraintIndices[i]);
+
+ for (size_t i = 0; i < cloth->mConstraintCoefficients.size(); ++i)
+ g_buffers->springStiffness.push_back(cloth->mConstraintCoefficients[i]);
+
+ for (size_t i = 0; i < cloth->mConstraintRestLengths.size(); ++i)
+ g_buffers->springLengths.push_back(cloth->mConstraintRestLengths[i]);
+
+ mCloths.push_back(cloth);
+
+ // add inflatable params
+ g_buffers->inflatableVolumes.push_back(cloth->mRestVolume);
+ g_buffers->inflatableCoefficients.push_back(cloth->mConstraintScale);
+ }
+
+ void Initialize()
+ {
+ mCloths.resize(0);
+
+ float minSize = 0.75f;
+ float maxSize = 1.0f;
+
+ // convex rocks
+ for (int i = 0; i < 5; i++)
+ AddRandomConvex(10, Vec3(i*2.0f, 0.0f, Randf(0.0f, 2.0f)), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi));
+
+ float radius = 0.12f;
+ int group = 0;
+
+ const char* meshes[2] =
+ {
+ "../../data/box_high_weld.ply",
+ "../../data/sphere.ply"
+ };
+
+ mPressure = 1.0f;
+
+ for (int y = 0; y < 2; ++y)
+ {
+ for (int i = 0; i < 4; ++i)
+ {
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform(meshes[(i + y) & 1]).c_str());
+ mesh->Normalize();
+ mesh->Transform(TranslationMatrix(Point3(i*2.0f, 1.0f + y*2.0f, 1.5f)));
+
+ AddInflatable(mesh, mPressure, NvFlexMakePhase(group++, 0));
+
+ delete mesh;
+ }
+ }
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.4f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 10;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.drag = 0.0f;
+ g_params.collisionDistance = 0.01f;
+
+ // better convergence with global relaxation factor
+ g_params.relaxationMode = eNvFlexRelaxationGlobal;
+ g_params.relaxationFactor = 0.25f;
+
+ g_windStrength = 0.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = 0;
+ g_drawCloth = false;
+ }
+
+ virtual void DoGui()
+ {
+ if (imguiSlider("Over Pressure", &mPressure, 0.25f, 3.0f, 0.001f))
+ {
+ for (int i = 0; i < int(g_buffers->inflatablePressures.size()); ++i)
+ g_buffers->inflatablePressures[i] = mPressure;
+ }
+ }
+
+ virtual void Sync()
+ {
+ NvFlexSetInflatables(g_flex, g_buffers->inflatableTriOffsets.buffer, g_buffers->inflatableTriCounts.buffer, g_buffers->inflatableVolumes.buffer, g_buffers->inflatablePressures.buffer, g_buffers->inflatableCoefficients.buffer, mCloths.size());
+ }
+
+ virtual void Draw(int pass)
+ {
+ if (!g_drawMesh)
+ return;
+
+ int indexStart = 0;
+
+ for (size_t i = 0; i < mCloths.size(); ++i)
+ {
+ DrawCloth(&g_buffers->positions[0], &g_buffers->normals[0], NULL, &g_buffers->triangles[indexStart], mCloths[i]->mTris.size(), g_buffers->positions.size(), i % 6, g_params.radius*0.35f);
+
+ indexStart += mCloths[i]->mTris.size() * 3;
+ }
+ }
+
+ float mPressure;
+
+ std::vector<ClothMesh*> mCloths;
+};
diff --git a/demo/scenes/initialoverlap.h b/demo/scenes/initialoverlap.h
new file mode 100644
index 0000000..5c3abe4
--- /dev/null
+++ b/demo/scenes/initialoverlap.h
@@ -0,0 +1,30 @@
+
+
+// tests initial particle overlap, particle should be projected out of the box without high velocity
+class InitialOverlap : public Scene
+{
+public:
+
+ InitialOverlap(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ g_params.radius = 0.1f;
+ g_params.numIterations = 2;
+
+ // test max acceleration clamping is working, test at 5x gravity
+ g_params.maxAcceleration = 50.0f;
+
+ // plinth
+ AddBox(1.0f, Vec3(0.0f, 0.0f, 0.0f));
+
+ g_buffers->positions.push_back(Vec4(0.0f, 0.5f, 0.0f, 1.0f));
+ g_buffers->velocities.push_back(Vec3(0.0f));
+ g_buffers->phases.push_back(0);
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = true;
+ }
+};
diff --git a/demo/scenes/lighthouse.h b/demo/scenes/lighthouse.h
new file mode 100644
index 0000000..e4b410a
--- /dev/null
+++ b/demo/scenes/lighthouse.h
@@ -0,0 +1,59 @@
+
+
+class Lighthouse : public Scene
+{
+public:
+
+ Lighthouse(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.15f;
+ float restDistance = radius*0.6f;
+
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/lighthouse.ply").c_str(), 128);
+ AddSDF(sdf, Vec3(4.0f, 0.0f, 0.0f), Quat(), 10.f);
+
+ CreateParticleGrid(Vec3(0.0f, 0.3f, 0.0f), 48, 48, 128, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), 0.005f);
+
+ g_sceneLower = 0.0f;
+ g_sceneUpper = Vec3(12, 0.0f, 0.0f);
+
+ g_numSubsteps = 2;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.f;
+ g_params.fluid = true;
+ g_params.viscosity = 0.01f;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 50.0f;
+ g_params.anisotropyScale = 20.0f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.gravity[1] *= 0.5f;
+ g_params.cohesion *= 0.5f;
+
+ g_fluidColor = Vec4(0.413f, 0.725f, 0.85f, 0.7f);
+
+ g_maxDiffuseParticles = 1024 * 1024;
+ g_diffuseScale = 0.3f;
+ g_diffuseShadow = true;
+ g_diffuseColor = 1.0f;
+ g_diffuseMotionScale = 1.0f;
+ g_params.diffuseThreshold *= 10.f;
+ g_params.diffuseBallistic = 4;
+ g_params.diffuseBuoyancy = 2.0f;
+ g_params.diffuseDrag = 1.0f;
+
+ g_params.numPlanes = 5;
+
+ g_waveFrequency = 1.2f;
+ g_waveAmplitude = 2.2f;
+ g_waveFloorTilt = 0.1f;
+
+
+ // draw options
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ g_drawDiffuse = true;
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/localspacecloth.h b/demo/scenes/localspacecloth.h
new file mode 100644
index 0000000..f28912e
--- /dev/null
+++ b/demo/scenes/localspacecloth.h
@@ -0,0 +1,134 @@
+
+
+
+class LocalSpaceCloth : public Scene
+{
+public:
+
+ LocalSpaceCloth (const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 1.0f;
+ float shearStiffness = 1.0f;
+
+ float radius = 0.1f;
+
+ CreateSpringGrid(Vec3(0.5f, 1.45f, -0.5f), 32, 20, 1, radius*0.5f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), 1.0f);
+
+ int c1 = 1;
+ int c2 = 20;
+
+ // add tethers
+ for (int i=0; i < int(g_buffers->positions.size()); ++i)
+ {
+ float minSqrDist = FLT_MAX;
+
+ if (i != c1 && i != c2)
+ {
+ float stiffness = -0.8f;
+ float give = 0.01f;
+
+ float sqrDist = LengthSq(Vec3(g_buffers->positions[c1])-Vec3(g_buffers->positions[c2]));
+
+ if (sqrDist < minSqrDist)
+ {
+ CreateSpring(c1, i, stiffness, give);
+ CreateSpring(c2, i, stiffness, give);
+
+ minSqrDist = sqrDist;
+ }
+ }
+ }
+
+
+
+ for (int i=0; i < g_buffers->positions.size(); ++i)
+ {
+ if (g_buffers->positions[i].x == 0.5f)
+ g_buffers->positions[i].w = 0.0f;
+ }
+
+ translation = Vec3(0.0f, 1.0f, 0.0f);
+ size = Vec3(0.5f, 1.1f, 0.6f);
+ rotation = 0.0f;
+ rotationSpeed = 0.0f;
+
+ linearInertialScale = 0.25f;
+ angularInertialScale = 0.25f;
+
+ AddBox(size, translation);
+
+ // initialize our moving frame to the center of the box
+ NvFlexExtMovingFrameInit(&meshFrame, translation, Quat());
+
+ g_numSubsteps = 2;
+
+ g_params.fluid = false;
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.f;
+ g_params.restitution = 0.0f;
+ g_params.shapeCollisionMargin = 0.05f;
+
+ g_params.numIterations = 6;
+
+ // draw options
+ g_drawPoints = false;
+ }
+
+ virtual void DoGui()
+ {
+ imguiSlider("Rotation", &rotationSpeed, 0.0f, 20.0f, 0.1f);
+ imguiSlider("Translation", &translation.x, -2.0f, 2.0f, 0.001f);
+
+ imguiSlider("Linear Inertia", &linearInertialScale, 0.0f, 1.0f, 0.001f);
+ imguiSlider("Angular Inertia", &angularInertialScale, 0.0f, 1.0f, 0.001f);
+
+ }
+
+ virtual void Update()
+ {
+ rotation += rotationSpeed*g_dt;
+
+ // new position of the box center
+ Vec3 newPosition = translation;//meshFrame.GetPosition() + Vec3(float(g_lastdx), 0.0f, float(g_lastdy))*0.001f;
+ Quat newRotation = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), rotation);
+
+ NvFlexExtMovingFrameUpdate(&meshFrame, newPosition, newRotation, g_dt);
+
+ // update all the particles in the sim with inertial forces
+ NvFlexExtMovingFrameApply(
+ &meshFrame,
+ &g_buffers->positions[0].x,
+ &g_buffers->velocities[0].x,
+ g_buffers->positions.size(),
+ linearInertialScale,
+ angularInertialScale,
+ g_dt);
+
+ // update collision shapes
+ g_buffers->shapePositions.resize(0);
+ g_buffers->shapeRotations.resize(0);
+ g_buffers->shapePrevPositions.resize(0);
+ g_buffers->shapePrevRotations.resize(0);
+ g_buffers->shapeGeometry.resize(0);
+ g_buffers->shapeFlags.resize(0);
+
+ AddBox(size, newPosition, newRotation);
+
+ UpdateShapes();
+ }
+
+ Vec3 size;
+ Vec3 translation;
+ float rotation;
+ float rotationSpeed;
+
+ float linearInertialScale;
+ float angularInertialScale;
+
+ NvFlexExtMovingFrame meshFrame;
+
+ NvFlexTriangleMeshId mesh;
+};
diff --git a/demo/scenes/localspacefluid.h b/demo/scenes/localspacefluid.h
new file mode 100644
index 0000000..494249a
--- /dev/null
+++ b/demo/scenes/localspacefluid.h
@@ -0,0 +1,127 @@
+
+
+class LocalSpaceFluid : public Scene
+{
+public:
+
+ LocalSpaceFluid (const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ const float radius = 0.05f;
+ const float restDistance = radius*0.6f;
+
+ int dx = int(ceilf(1.f / restDistance));
+ int dy = int(ceilf(1.f / restDistance));
+ int dz = int(ceilf(1.f / restDistance));
+
+ CreateParticleGrid(Vec3(0.0f, 1.0f, 0.0f), dx, dy, dz, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Vec3 center = (lower+upper)*0.5f;
+
+ Mesh* shape = ImportMesh("../../data/torus.obj");
+ shape->Transform(ScaleMatrix(Vec3(0.7f)));
+
+ //Mesh* box = ImportMesh("../../data/sphere.ply");
+ //box->Transform(TranslationMatrix(Point3(0.0f, 0.1f, 0.0f))*ScaleMatrix(Vec3(1.5f)));
+
+ // invert box faces
+ for (int i=0; i < int(shape->GetNumFaces()); ++i)
+ swap(shape->m_indices[i*3+0], shape->m_indices[i*3+1]);
+
+ shape->CalculateNormals();
+
+ // shift into torus interior
+ for (int i=0; i < g_buffers->positions.size(); ++i)
+ (Vec3&)(g_buffers->positions[i]) -= Vec3(2.1f, 0.0f, 0.0f);
+
+ mesh = CreateTriangleMesh(shape);
+ AddTriangleMesh(mesh, Vec3(center), Quat(), 1.0f);
+
+ // initialize our moving frame to the center of the box
+ NvFlexExtMovingFrameInit(&meshFrame, center, Quat());
+
+ g_numSubsteps = 2;
+
+ g_params.fluid = true;
+ g_params.radius = radius;
+ g_params.fluidRestDistance = restDistance;
+ g_params.dynamicFriction = 0.f;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = 0.05f;
+ g_params.shapeCollisionMargin = 0.00001f;
+ g_params.maxSpeed = g_numSubsteps*restDistance/g_dt;
+
+ g_params.numIterations = 4;
+
+ g_params.smoothing = 0.4f;
+ g_params.anisotropyScale = 3.0f / radius;
+ g_params.viscosity = 0.001f;
+ g_params.cohesion = 0.05f;
+ g_params.surfaceTension = 0.0f;
+
+ translation = center;
+ rotation = 0.0f;
+ rotationSpeed = 0.0f;
+
+ linearInertialScale = 0.25f;
+ angularInertialScale = 0.75f;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ g_drawDiffuse = true;
+ }
+
+ virtual void DoGui()
+ {
+ imguiSlider("Rotation", &rotationSpeed, 0.0f, 7.0f, 0.1f);
+ imguiSlider("Translation", &translation.x, -2.0f, 2.0f, 0.001f);
+ imguiSlider("Linear Inertia", &linearInertialScale, 0.0f, 1.0f, 0.001f);
+ imguiSlider("Angular Inertia", &angularInertialScale, 0.0f, 1.0f, 0.001f);
+ }
+
+ virtual void Update()
+ {
+ rotation += rotationSpeed*g_dt;
+
+ // new position of the box center
+ Vec3 newPosition = translation;//meshFrame.GetPosition() + Vec3(float(g_lastdx), 0.0f, float(g_lastdy))*0.001f;
+ Quat newRotation = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), rotation);
+
+ NvFlexExtMovingFrameUpdate(&meshFrame, newPosition, newRotation, g_dt);
+
+ // update all the particles in the sim with inertial forces
+ NvFlexExtMovingFrameApply(
+ &meshFrame,
+ &g_buffers->positions[0].x,
+ &g_buffers->velocities[0].x,
+ g_buffers->positions.size(),
+ linearInertialScale,
+ angularInertialScale,
+ g_dt);
+
+ // update torus transform
+ g_buffers->shapePrevPositions[0] = g_buffers->shapePositions[0];
+ g_buffers->shapePrevRotations[0] = g_buffers->shapeRotations[0];
+
+ g_buffers->shapePositions[0] = Vec4(newPosition, 1.0f);
+ g_buffers->shapeRotations[0] = newRotation;
+
+ UpdateShapes();
+ }
+
+ Vec3 translation;
+ float rotation;
+ float rotationSpeed;
+
+ float linearInertialScale;
+ float angularInertialScale;
+
+ NvFlexExtMovingFrame meshFrame;
+
+ NvFlexTriangleMeshId mesh;
+}; \ No newline at end of file
diff --git a/demo/scenes/lowdimensionalshapes.h b/demo/scenes/lowdimensionalshapes.h
new file mode 100644
index 0000000..5d32ea5
--- /dev/null
+++ b/demo/scenes/lowdimensionalshapes.h
@@ -0,0 +1,54 @@
+
+class LowDimensionalShapes: public Scene
+{
+public:
+
+ LowDimensionalShapes(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.1f;
+ int group = 0;
+
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform("../../data/box.ply").c_str());
+
+ CreateParticleShape(mesh, Vec3(0.0f, 1.0f, 0.0f), Vec3(1.2f, 0.001f, 1.2f), 0.0f, radius, Vec3(0.0f, 0.0f, 0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+
+ for (int i=0; i < 64; ++i)
+ CreateParticleShape(mesh, Vec3(i / 8 * radius, 0.0f, i % 8 * radius), Vec3(0.1f, 0.8f, 0.1f), 0.0f, radius, Vec3(0.0f, 0.0f, 0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+
+ delete mesh;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 1.0f;
+ g_params.fluid = false;
+ g_params.fluidRestDistance = radius;
+ g_params.viscosity = 0.0f;
+ g_params.numIterations = 4;
+ g_params.vorticityConfinement = 0.f;
+ g_params.anisotropyScale = 20.0f;
+ g_params.numPlanes = 1;
+ g_params.collisionDistance = radius*0.5f;
+ g_params.shockPropagation = 5.0f;
+
+ g_numSubsteps = 2;
+
+ g_maxDiffuseParticles = 0;
+ g_diffuseScale = 0.75f;
+
+ g_lightDistance *= 1.5f;
+
+ g_fluidColor = Vec4(0.2f, 0.6f, 0.9f, 1.0f);
+
+ // draw options
+ g_drawDensity = false;
+ g_drawDiffuse = false;
+ g_drawEllipsoids = false;
+ g_drawPoints = true;
+ g_drawMesh = false;
+
+ g_warmup = false;
+
+ }
+
+};
diff --git a/demo/scenes/melting.h b/demo/scenes/melting.h
new file mode 100644
index 0000000..c7da792
--- /dev/null
+++ b/demo/scenes/melting.h
@@ -0,0 +1,70 @@
+
+class Melting : public Scene
+{
+public:
+
+ Melting(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ g_params.radius = 0.1f;
+
+ g_params.numIterations = 2;
+ g_params.dynamicFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.viscosity = 0.0f;
+ g_params.fluid = true;
+ g_params.cohesion = 0.0f;
+ g_params.fluidRestDistance = g_params.radius*0.6f;
+ g_params.anisotropyScale = 4.0f / g_params.radius;
+ g_params.smoothing = 0.5f;
+
+ const float spacing = g_params.radius*0.5f;
+
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform("../../data/bunny.ply").c_str());
+
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid);
+ float size = 1.2f;
+
+ for (int i = 0; i < 1; ++i)
+ for (int j = 0; j < 3; ++j)
+ CreateParticleShape(mesh, Vec3(-2.0f + j*size, 3.0f + j*size, i*size), size, 0.0f, spacing, Vec3(0.0f, 0.0f, 0.0f), 1.0f, true, 1.f, phase, false, 0.0f);
+
+ delete mesh;
+
+ // plinth
+ AddBox(2.0f, Vec3(0.0f, 1.0f, 0.0f));
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = true;
+ g_drawMesh = false;
+
+ mFrame = 0;
+ }
+
+ virtual void Update()
+ {
+ const int start = 130;
+
+ if (mFrame >= start)
+ {
+ float stiffness = max(0.0f, 1.0f - (mFrame - start) / 100.0f);
+
+ for (int i = 0; i < g_buffers->rigidCoefficients.size(); ++i)
+ g_buffers->rigidCoefficients[i] = stiffness;
+
+ g_params.cohesion = Lerp(0.05f, 0.0f, stiffness);
+ }
+
+ ++mFrame;
+ }
+
+ virtual void Sync()
+ {
+ NvFlexSetRigids(g_flex, g_buffers->rigidOffsets.buffer, g_buffers->rigidIndices.buffer, g_buffers->rigidLocalPositions.buffer, g_buffers->rigidLocalNormals.buffer, g_buffers->rigidCoefficients.buffer, g_buffers->rigidRotations.buffer, g_buffers->rigidTranslations.buffer, g_buffers->rigidOffsets.size() - 1, g_buffers->rigidIndices.size());
+ }
+
+ int mFrame;
+}; \ No newline at end of file
diff --git a/demo/scenes/mixedpile.h b/demo/scenes/mixedpile.h
new file mode 100644
index 0000000..61bc5a5
--- /dev/null
+++ b/demo/scenes/mixedpile.h
@@ -0,0 +1,221 @@
+
+/*
+class MixedPile : public Scene
+{
+public:
+
+ MixedPile(const char* name) : Scene(name)
+ {
+ }
+
+
+ std::vector<ClothMesh*> mCloths;
+ std::vector<float> mRestVolume;
+ std::vector<int> mTriOffset;
+ std::vector<int> mTriCount;
+ std::vector<float> mOverPressure;
+ std::vector<float> mConstraintScale;
+ std::vector<float> mSplitThreshold;
+
+ void AddInflatable(const Mesh* mesh, float overPressure, int phase, float invmass=1.0f)
+ {
+ const int startVertex = g_buffers->positions.size();
+
+ // add mesh to system
+ for (size_t i=0; i < mesh->GetNumVertices(); ++i)
+ {
+ const Vec3 p = Vec3(mesh->m_positions[i]);
+
+ g_buffers->positions.push_back(Vec4(p.x, p.y, p.z, invmass));
+ g_buffers->velocities.push_back(0.0f);
+ g_buffers->phases.push_back(phase);
+ }
+
+ int triOffset = g_buffers->triangles.size();
+ int triCount = mesh->GetNumFaces();
+
+ mTriOffset.push_back(triOffset/3);
+ mTriCount.push_back(mesh->GetNumFaces());
+ mOverPressure.push_back(overPressure);
+
+ for (size_t i=0; i < mesh->m_indices.size(); i+=3)
+ {
+ int a = mesh->m_indices[i+0];
+ int b = mesh->m_indices[i+1];
+ int c = mesh->m_indices[i+2];
+
+ Vec3 n = -Normalize(Cross(mesh->m_positions[b]-mesh->m_positions[a], mesh->m_positions[c]-mesh->m_positions[a]));
+ g_buffers->triangleNormals.push_back(n);
+
+ g_buffers->triangles.push_back(a + startVertex);
+ g_buffers->triangles.push_back(b + startVertex);
+ g_buffers->triangles.push_back(c + startVertex);
+ }
+
+ // create a cloth mesh using the global positions / indices
+ ClothMesh* cloth = new ClothMesh(&g_buffers->positions[0], g_buffers->positions.size(), &g_buffers->triangles[triOffset],triCount*3, 0.8f, 1.0f);
+
+ for (size_t i=0; i < cloth->mConstraintIndices.size(); ++i)
+ g_buffers->springIndices.push_back(cloth->mConstraintIndices[i]);
+
+ for (size_t i=0; i < cloth->mConstraintCoefficients.size(); ++i)
+ g_buffers->springStiffness.push_back(cloth->mConstraintCoefficients[i]);
+
+ for (size_t i=0; i < cloth->mConstraintRestLengths.size(); ++i)
+ g_buffers->springLengths.push_back(cloth->mConstraintRestLengths[i]);
+
+ mCloths.push_back(cloth);
+
+ // add inflatable params
+ mRestVolume.push_back(cloth->mRestVolume);
+ mConstraintScale.push_back(cloth->mConstraintScale);
+ }
+
+
+ virtual void Initialize()
+ {
+
+ Vec3 start(0.0f, 0.5f + g_params.radius*0.25f, 0.0f);
+
+ float radius = g_params.radius;
+
+ int group = 1;
+
+ if (1)
+ {
+ mCloths.resize(0);
+ mRestVolume.resize(0);
+ mTriOffset.resize(0);
+ mTriCount.resize(0);
+ mOverPressure.resize(0);
+ mConstraintScale.resize(0);
+ mSplitThreshold.resize(0);
+
+ Vec3 lower(0.0f), upper(0.0f);
+ float size = 1.0f + radius;
+
+ for (int i=0; i < 9; ++i)
+ {
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform("../../data/sphere.ply").c_str());
+ mesh->Normalize();
+ mesh->Transform(TranslationMatrix(Point3(lower.x + i%3*size, upper.y + 2.0f, (upper.z+lower.z)*0.5f + i/3*size)));
+
+ AddInflatable(mesh, 1.0f, NvFlexMakePhase(group++, 0), 2.0f);
+ delete mesh;
+ }
+ }
+
+
+ if (1)
+ {
+ const int minSize[3] = { 2, 1, 3 };
+ const int maxSize[3] = { 4, 3, 6 };
+
+ Vec4 color = Vec4(SrgbToLinear(Colour(Vec4(201.0f, 158.0f, 106.0f, 255.0f)/255.0f)));
+
+ Vec3 lower(0.0f), upper(5.0f);
+ GetParticleBounds(lower,upper);
+
+ int dimx = 3;
+ int dimy = 10;
+ int dimz = 3;
+
+ for (int y=0; y < dimy; ++y)
+ {
+ for (int z=0; z < dimz; ++z)
+ {
+ for (int x=0; x < dimx; ++x)
+ {
+ CreateParticleShape(
+ GetFilePathByPlatform("../../data/box.ply").c_str(),
+ Vec3(x + 0.5f,0,z+ 0.5f)*(1.0f+radius) + Vec3(0.0f, upper.y + (y+2.0f)*maxSize[1]*g_params.radius, 0.0f),
+ Vec3(float(Rand(minSize[0], maxSize[0])),
+ float(Rand(minSize[1], maxSize[1])),
+ float(Rand(minSize[2], maxSize[2])))*g_params.radius*0.9f, 0.0f, g_params.radius*0.9f, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), true,0.0f,0.0f, 0.0f, color);
+ }
+ }
+ }
+ }
+
+
+ if (1)
+ {
+ Vec3 lower, upper;
+ GetParticleBounds(lower,upper);
+ Vec3 center = (upper+lower)*0.5f;
+ center.y = upper.y;
+
+ for (int i=0; i < 20; ++i)
+ {
+ Rope r;
+ Vec3 offset = Vec3(sinf(k2Pi*float(i)/20), 0.0f, cosf(k2Pi*float(i)/20));
+
+ CreateRope(r, center + offset, Normalize(offset + Vec3(0.0f, 4.0f, 0.0f)), 1.2f, 50, 50*radius, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide), 0.0f, 10.0f, 0.0f);
+ g_ropes.push_back(r);
+ }
+ }
+
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Vec3 center = (lower+upper)*0.5f;
+ center.y = 0.0f;
+
+ float width = (upper-lower).x;
+ float edge = 0.25f;
+ float height = 1.0f;
+ AddBox(Vec3(edge, height, width), center + Vec3(-width, height/2.0f, 0.0f));
+ AddBox(Vec3(edge, height, width), center + Vec3(width, height/2.0f, 0.0f));
+
+ AddBox(Vec3(width-edge, height, edge), center + Vec3(0.0f, height/2.0f, width-edge));
+ AddBox(Vec3(width-edge, height, edge), center + Vec3(0.0f, height/2.0f, -(width-edge)));
+
+ //g_numExtraParticles = 32*1024;
+ g_numSubsteps = 2;
+ g_params.numIterations = 7;
+
+ g_params.radius *= 1.0f;
+ g_params.solidRestDistance = g_params.radius;
+ g_params.fluidRestDistance = g_params.radius*0.55f;
+ g_params.dynamicFriction = 0.6f;
+ g_params.staticFriction = 0.75f;
+ g_params.particleFriction = 0.3f;
+ g_params.dissipation = 0.0f;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.sleepThreshold = g_params.radius*0.125f;
+ g_params.shockPropagation = 0.0f;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = g_params.radius*0.5f;
+ g_params.fluid = false;
+ g_params.maxSpeed = 2.0f*g_params.radius*g_numSubsteps/g_dt;
+
+ // separte solid particle count
+ g_numSolidParticles = g_buffers->positions.size();
+ // number of fluid particles to allocate
+ g_numExtraParticles = 32*1024;
+
+ g_params.numPlanes = 1;
+ g_windStrength = 0.0f;
+
+ g_lightDistance *= 0.5f;
+
+ // draw options
+ g_drawPoints = true;
+ g_expandCloth = g_params.radius*0.5f;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.radius*0.5f/g_dt);
+ g_emitters[0].mSpeed = (g_params.radius/g_dt);
+
+ extern Colour gColors[];
+ gColors[0] = Colour(0.805f, 0.702f, 0.401f);
+ }
+
+ virtual void Update()
+ {
+ NvFlexSetInflatables(g_flex, &mTriOffset[0], &mTriCount[0], &mRestVolume[0], &mOverPressure[0], &mConstraintScale[0], mCloths.size(), eFlexMemoryHost);
+ }
+
+ int mHeight;
+};
+*/
diff --git a/demo/scenes/nonconvex.h b/demo/scenes/nonconvex.h
new file mode 100644
index 0000000..37a5a6d
--- /dev/null
+++ b/demo/scenes/nonconvex.h
@@ -0,0 +1,47 @@
+
+
+class NonConvex : public Scene
+{
+public:
+
+ NonConvex(const char* name) : Scene(name)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ float radius = 0.15f;
+ int group = 0;
+
+ for (int i = 0; i < 1; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/bowl.obj").c_str(), Vec3(0.0f, 1.0f + 0.5f*i + radius*0.5f, 0.0f), Vec3(1.5f), 0.0f, radius*0.8f, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f, Vec3(0.0f));
+
+ for (int i = 0; i < 50; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/banana.obj").c_str(), Vec3(0.4f, 2.5f + i*0.25f, 0.25f) + RandomUnitVector()*radius*0.25f, Vec3(1), 0.0f, radius, Vec3(0.0f), 1.0f, true, 0.5f, NvFlexMakePhase(group++, 0), true, radius*0.1f, 0.0f, 0.0f, 1.25f*Vec4(0.875f, 0.782f, 0.051f, 1.0f));
+
+ AddBox();
+
+ g_numSubsteps = 3;
+ g_params.numIterations = 3;
+
+ g_params.radius *= 1.0f;
+ g_params.dynamicFriction = 0.35f;
+ g_params.dissipation = 0.0f;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.sleepThreshold = g_params.radius*0.2f;
+ g_params.shockPropagation = 3.0f;
+ g_params.gravity[1] *= 1.0f;
+ g_params.restitution = 0.01f;
+ g_params.damping = 0.25f;
+
+ // draw options
+ g_drawPoints = false;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.radius*2.0f / g_dt);
+ }
+
+ virtual void Update()
+ {
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/parachutingbunnies.h b/demo/scenes/parachutingbunnies.h
new file mode 100644
index 0000000..04f2052
--- /dev/null
+++ b/demo/scenes/parachutingbunnies.h
@@ -0,0 +1,167 @@
+
+class ParachutingBunnies : public Scene
+{
+public:
+
+ ParachutingBunnies(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.8f;
+ float shearStiffness = 0.8f;
+
+ int dimx = 32;
+ int dimy = 32;
+ float radius = 0.055f;
+
+ float height = 10.0f;
+ float spacing = 1.5f;
+ int numBunnies = 2;
+ int group = 0;
+
+ for (int i=0; i < numBunnies; ++i)
+ {
+ CreateSpringGrid(Vec3(i*dimx*radius, height + i*spacing, 0.0f), dimx, dimy, 1, radius, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), 1.1f);
+
+ const int startIndex = i*dimx*dimy;
+
+ int corner0 = startIndex + 0;
+ int corner1 = startIndex + dimx-1;
+ int corner2 = startIndex + dimx*(dimy-1);
+ int corner3 = startIndex + dimx*dimy-1;
+
+ CreateSpring(corner0, corner1, 1.f,-0.1f);
+ CreateSpring(corner1, corner3, 1.f,-0.1f);
+ CreateSpring(corner3, corner2, 1.f,-0.1f);
+ CreateSpring(corner0, corner2, 1.f,-0.1f);
+ }
+
+ for (int i=0; i < numBunnies; ++i)
+ {
+ Vec3 velocity = RandomUnitVector()*1.0f;
+ float size = radius*8.5f;
+
+ CreateParticleShape(GetFilePathByPlatform("../../data/bunny.ply").c_str(), Vec3(i*dimx*radius + radius*0.5f*dimx - 0.5f*size, height + i*spacing-0.5f, radius*0.5f*dimy - 0.5f), size, 0.0f, radius, velocity, 0.15f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f);
+
+ const int startIndex = i*dimx*dimy;
+ const int attachIndex = g_buffers->positions.size()-1;
+ g_buffers->positions[attachIndex].w = 2.0f;
+
+ int corner0 = startIndex + 0;
+ int corner1 = startIndex + dimx-1;
+ int corner2 = startIndex + dimx*(dimy-1);
+ int corner3 = startIndex + dimx*dimy-1;
+
+ Vec3 attachPosition = (Vec3(g_buffers->positions[corner0]) + Vec3(g_buffers->positions[corner1]) + Vec3(g_buffers->positions[corner2]) + Vec3(g_buffers->positions[corner3]))*0.25f;
+ attachPosition.y = height + i*spacing-0.5f;
+
+ if (1)
+ {
+ int c[4] = {corner0, corner1, corner2, corner3};
+
+ for (int i=0; i < 4; ++i)
+ {
+ Rope r;
+
+ int start = g_buffers->positions.size();
+
+ r.mIndices.push_back(attachIndex);
+
+ Vec3 d0 = Vec3(g_buffers->positions[c[i]])-attachPosition;
+ CreateRope(r, attachPosition, Normalize(d0), 1.2f, int(Length(d0)/radius*1.1f), Length(d0), NvFlexMakePhase(group++, 0), 0.0f, 0.5f, 0.0f);
+
+ r.mIndices.push_back(c[i]);
+ g_ropes.push_back(r);
+
+ int end = g_buffers->positions.size()-1;
+
+
+ CreateSpring(attachIndex, start, 1.2f, -0.5f);
+ CreateSpring(c[i], end, 1.0f);
+ }
+ }
+ }
+
+ if (1)
+ {
+ // falling objects
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Vec3 center = (lower+upper)*0.5f;
+ center.y = 0.0f;
+
+ float width = (upper-lower).x*0.5f;
+ float edge = 0.125f;
+ float height = 0.5f;
+
+ // big blocks
+ for (int i=0; i < 3; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), center + Vec3(float(i)-1.0f, 5.0f, 0.0f), radius*9, 0.0f, radius*0.9f, Vec3(0.0f), 0.5f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f, 0.0f, -radius*1.5f);
+
+ // small blocks
+ for (int j=0; j < 2; ++j)
+ for (int i=0; i < 8; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), Vec3(lower.x + 0.5f, 0.0f, lower.z - 0.5f) + Vec3(float(i/3), 6.0f + float(j), float(i%3)) + RandomUnitVector()*0.5f, radius*4, 0.0f, radius*0.9f, Vec3(0.0f), 1.f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f, 0.0f, -radius*2.0f);
+
+ g_numSolidParticles = g_buffers->positions.size();
+
+ {
+ AddBox(Vec3(edge, height, width+edge*2.0f), center + Vec3(-width - edge, height/2.0f, 0.0f));
+ AddBox(Vec3(edge, height, width+edge*2.0f), center + Vec3(width + edge, height/2.0f, 0.0f));
+
+ AddBox(Vec3(width+2.0f*edge, height, edge), center + Vec3(0.0f, height/2.0f, -(width+edge)));
+ AddBox(Vec3(width+2.0f*edge, height, edge), center + Vec3(0.0f, height/2.0f, width+edge));
+
+ float fluidWidth = width;
+ float fluidHeight = height*1.25f;
+
+ int particleWidth = int(2.0f*fluidWidth/radius);
+ int particleHeight = int(fluidHeight/radius);
+
+ CreateParticleGrid(center - Vec3(fluidWidth, 0.0f, fluidWidth), particleWidth, particleHeight, particleWidth, radius, Vec3(0.0f), 2.0f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid));
+ }
+ }
+
+ g_params.fluid = true;
+ g_params.radius = 0.1f;
+ g_params.fluidRestDistance = radius;
+ g_params.numIterations = 4;
+ g_params.viscosity = 1.0f;
+ g_params.dynamicFriction = 0.05f;
+ g_params.staticFriction = 0.0f;
+ g_params.particleCollisionMargin = 0.0f;
+ g_params.collisionDistance = g_params.fluidRestDistance*0.5f;
+ g_params.vorticityConfinement = 120.0f;
+ g_params.cohesion = 0.0025f;
+ g_params.drag = 0.06f;
+ g_params.lift = 0.f;
+ g_params.solidPressure = 0.0f;
+ g_params.anisotropyScale = 22.0f;
+ g_params.smoothing = 1.0f;
+ g_params.relaxationFactor = 1.0f;
+
+ g_maxDiffuseParticles = 64*1024;
+ g_diffuseScale = 0.25f;
+ g_diffuseShadow = false;
+ g_diffuseColor = 2.5f;
+ g_diffuseMotionScale = 1.5f;
+ g_params.diffuseThreshold *= 0.01f;
+ g_params.diffuseBallistic = 35;
+
+ g_windStrength = 0.0f;
+ g_windFrequency = 0.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawEllipsoids = true;
+ g_drawPoints = false;
+ g_drawDiffuse = true;
+ g_drawSprings = 0;
+
+ g_ropeScale = 0.2f;
+ g_warmup = false;
+ }
+};
diff --git a/demo/scenes/pasta.h b/demo/scenes/pasta.h
new file mode 100644
index 0000000..3852029
--- /dev/null
+++ b/demo/scenes/pasta.h
@@ -0,0 +1,47 @@
+
+class Pasta : public Scene
+{
+public:
+
+ Pasta(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.1f;
+ float length = 15.0f;
+ int n = 20;
+
+ for (int i = 0; i < n; ++i)
+ {
+ float theta = k2Pi*float(i) / n;
+
+ Rope r;
+ CreateRope(r, 0.5f*Vec3(cosf(theta), 2.0f, sinf(theta)), Vec3(0.0f, 1.0f, 0.0f), 0.25f, int(length / radius), length, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide));
+ g_ropes.push_back(r);
+ }
+
+ g_numSubsteps = 3;
+
+ Mesh* bowl = ImportMesh(GetFilePathByPlatform("../../data/bowl.obj").c_str());
+ bowl->Normalize(2.0f);
+ bowl->CalculateNormals();
+ bowl->Transform(TranslationMatrix(Point3(-1.0f, 0.0f, -1.0f)));
+
+ NvFlexTriangleMeshId mesh = CreateTriangleMesh(bowl);
+ AddTriangleMesh(mesh, Vec3(), Quat(), 1.0f);
+
+ delete bowl;
+
+ g_params.numIterations = 6;
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.4f;
+ g_params.dissipation = 0.001f;
+ g_params.sleepThreshold = g_params.radius*0.2f;
+ g_params.relaxationFactor = 1.3f;
+ g_params.restitution = 0.0f;
+ g_params.shapeCollisionMargin = 0.01f;
+
+ g_lightDistance *= 0.5f;
+ g_drawPoints = false;
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/plasticbody.h b/demo/scenes/plasticbody.h
new file mode 100644
index 0000000..d5337d2
--- /dev/null
+++ b/demo/scenes/plasticbody.h
@@ -0,0 +1,271 @@
+
+
+class PlasticBody : public Scene
+{
+public:
+
+ PlasticBody(const char* name, const char* mesh) :
+ Scene(name),
+ mFile(mesh),
+ mScale(2.0f),
+ mOffset(0.0f, 1.0f, 0.0f),
+ mRadius(0.1f),
+ mClusterSpacing(1.0f),
+ mClusterRadius(0.0f),
+ mClusterStiffness(0.5f),
+ mLinkRadius(0.0f),
+ mLinkStiffness(1.0f),
+ mGlobalStiffness(1.0f),
+ mSurfaceSampling(0.0f),
+ mVolumeSampling(4.0f),
+ mSkinningFalloff(2.0f),
+ mSkinningMaxDistance(100.0f)
+ {
+ mStack[0] = 1;
+ mStack[1] = 1;
+ mStack[2] = 1;
+ }
+
+ virtual void Initialize()
+ {
+ float radius = mRadius;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.35f;
+ g_params.particleFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 4;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.0f;
+ g_params.lift = 0.0f;
+ g_params.collisionDistance = radius*0.75f;
+
+ g_params.plasticThreshold = 0.0015f;
+ g_params.plasticCreep = 0.125f;
+
+ g_params.relaxationFactor = 0.6f;
+
+ g_windStrength = 0.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ g_wireframe = false;
+ g_drawSprings = false;
+ g_drawBases = false;
+
+ g_buffers->rigidOffsets.push_back(0);
+
+ mInstances.resize(0);
+
+
+ CreateBodies();
+
+ AddPlinth();
+
+ // fix any particles below the ground plane in place
+ for (int i = 0; i < int(g_buffers->positions.size()); ++i)
+ if (g_buffers->positions[i].y < 0.0f)
+ g_buffers->positions[i].w = 0.0f;
+
+ // expand radius for better self collision
+ g_params.radius *= 1.5f;
+
+ g_lightDistance *= 1.5f;
+ }
+
+ virtual void CreateBodies()
+ {
+ // build soft body
+ for (int x = 0; x < mStack[0]; ++x)
+ {
+ for (int y = 0; y < mStack[1]; ++y)
+ {
+ for (int z = 0; z < mStack[2]; ++z)
+ {
+ CreatePlasticBody(mRadius, mOffset + Vec3(x*(mScale.x + 1), y*(mScale.y + 1), z*(mScale.z + 1))*mRadius, mClusterStiffness, mInstances.size());
+ }
+ }
+ }
+ }
+
+ void CreatePlasticBody(float radius, Vec3 position, float clusterStiffness, int group = 0)
+ {
+ Instance instance;
+
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform(mFile).c_str());
+ mesh->Normalize();
+ mesh->Transform(TranslationMatrix(Point3(position))*ScaleMatrix(mScale*radius));
+
+ instance.mMesh = mesh;
+ instance.mColor = Vec3(0.5f, 0.5f, 1.0f);
+ instance.mOffset = g_buffers->rigidTranslations.size();
+
+ double createStart = GetSeconds();
+
+ // create soft body definition
+ NvFlexExtAsset* asset = NvFlexExtCreateSoftFromMesh(
+ (float*)&instance.mMesh->m_positions[0],
+ instance.mMesh->m_positions.size(),
+ (int*)&instance.mMesh->m_indices[0],
+ instance.mMesh->m_indices.size(),
+ radius,
+ mVolumeSampling,
+ mSurfaceSampling,
+ mClusterSpacing*radius,
+ mClusterRadius*radius,
+ clusterStiffness,
+ mLinkRadius*radius,
+ mLinkStiffness,
+ mGlobalStiffness);
+
+ double createEnd = GetSeconds();
+
+ // create skinning
+ const int maxWeights = 4;
+
+ instance.mSkinningIndices.resize(instance.mMesh->m_positions.size()*maxWeights);
+ instance.mSkinningWeights.resize(instance.mMesh->m_positions.size()*maxWeights);
+
+ for (int i = 0; i < asset->numShapes; ++i)
+ instance.mRigidRestPoses.push_back(Vec3(&asset->shapeCenters[i * 3]));
+
+ double skinStart = GetSeconds();
+
+ NvFlexExtCreateSoftMeshSkinning(
+ (float*)&instance.mMesh->m_positions[0],
+ instance.mMesh->m_positions.size(),
+ asset->shapeCenters,
+ asset->numShapes,
+ mSkinningFalloff,
+ mSkinningMaxDistance,
+ &instance.mSkinningWeights[0],
+ &instance.mSkinningIndices[0]);
+
+ double skinEnd = GetSeconds();
+
+ printf("Created soft in %f ms Skinned in %f\n", (createEnd - createStart)*1000.0f, (skinEnd - skinStart)*1000.0f);
+
+ const int particleOffset = g_buffers->positions.size();
+ const int indexOffset = g_buffers->rigidOffsets.back();
+
+ // add particle data to solver
+ for (int i = 0; i < asset->numParticles; ++i)
+ {
+ g_buffers->positions.push_back(&asset->particles[i * 4]);
+ g_buffers->velocities.push_back(0.0f);
+
+ const int phase = NvFlexMakePhase(group, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+ g_buffers->phases.push_back(phase);
+ }
+
+ // add shape data to solver
+ for (int i = 0; i < asset->numShapeIndices; ++i)
+ g_buffers->rigidIndices.push_back(asset->shapeIndices[i] + particleOffset);
+
+ for (int i = 0; i < asset->numShapes; ++i)
+ {
+ g_buffers->rigidOffsets.push_back(asset->shapeOffsets[i] + indexOffset);
+ g_buffers->rigidTranslations.push_back(Vec3(&asset->shapeCenters[i * 3]));
+ g_buffers->rigidRotations.push_back(Quat());
+ g_buffers->rigidCoefficients.push_back(asset->shapeCoefficients[i]);
+ }
+
+ // add link data to the solver
+ for (int i = 0; i < asset->numSprings; ++i)
+ {
+ g_buffers->springIndices.push_back(asset->springIndices[i * 2 + 0]);
+ g_buffers->springIndices.push_back(asset->springIndices[i * 2 + 1]);
+
+ g_buffers->springStiffness.push_back(asset->springCoefficients[i]);
+ g_buffers->springLengths.push_back(asset->springRestLengths[i]);
+ }
+
+ NvFlexExtDestroyAsset(asset);
+
+ mInstances.push_back(instance);
+ }
+
+ virtual void Draw(int pass)
+ {
+ if (!g_drawMesh)
+ return;
+
+ for (int s = 0; s < int(mInstances.size()); ++s)
+ {
+ const Instance& instance = mInstances[s];
+
+ Mesh m;
+ m.m_positions.resize(instance.mMesh->m_positions.size());
+ m.m_normals.resize(instance.mMesh->m_normals.size());
+ m.m_indices = instance.mMesh->m_indices;
+
+ for (int i = 0; i < int(instance.mMesh->m_positions.size()); ++i)
+ {
+ Vec3 softPos;
+ Vec3 softNormal;
+
+ for (int w = 0; w < 4; ++w)
+ {
+ const int cluster = instance.mSkinningIndices[i * 4 + w];
+ const float weight = instance.mSkinningWeights[i * 4 + w];
+
+ if (cluster > -1)
+ {
+ // offset in the global constraint array
+ int rigidIndex = cluster + instance.mOffset;
+
+ Vec3 localPos = Vec3(instance.mMesh->m_positions[i]) - instance.mRigidRestPoses[cluster];
+
+ Vec3 skinnedPos = g_buffers->rigidTranslations[rigidIndex] + Rotate(g_buffers->rigidRotations[rigidIndex], localPos);
+ Vec3 skinnedNormal = Rotate(g_buffers->rigidRotations[rigidIndex], instance.mMesh->m_normals[i]);
+
+ softPos += skinnedPos*weight;
+ softNormal += skinnedNormal*weight;
+ }
+ }
+
+ m.m_positions[i] = Point3(softPos);
+ m.m_normals[i] = softNormal;
+ }
+
+ DrawMesh(&m, instance.mColor);
+ }
+ }
+
+ struct Instance
+ {
+ Mesh* mMesh;
+ std::vector<int> mSkinningIndices;
+ std::vector<float> mSkinningWeights;
+ vector<Vec3> mRigidRestPoses;
+ Vec3 mColor;
+ int mOffset;
+ };
+
+ std::vector<Instance> mInstances;
+
+ const char* mFile;
+ Vec3 mScale;
+ Vec3 mOffset;
+
+ float mRadius;
+
+ float mClusterSpacing;
+ float mClusterRadius;
+ float mClusterStiffness;
+
+ float mLinkRadius;
+ float mLinkStiffness;
+
+ float mGlobalStiffness;
+
+ float mSurfaceSampling;
+ float mVolumeSampling;
+
+ float mSkinningFalloff;
+ float mSkinningMaxDistance;
+
+ int mStack[3];
+}; \ No newline at end of file
diff --git a/demo/scenes/plasticstack.h b/demo/scenes/plasticstack.h
new file mode 100644
index 0000000..e1543ca
--- /dev/null
+++ b/demo/scenes/plasticstack.h
@@ -0,0 +1,50 @@
+
+class PlasticStack : public Scene
+{
+public:
+
+ PlasticStack(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ g_params.radius = 0.225f;
+
+ g_params.numIterations = 2;
+ g_params.dynamicFriction = 0.5f;
+ g_params.particleFriction = 0.15f;
+ g_params.dissipation = 0.0f;
+ g_params.viscosity = 0.0f;
+
+ AddPlinth();
+
+ const float rotation = -kPi*0.5f;
+ const float spacing = g_params.radius*0.5f;
+
+ // alternative box and sphere shapes
+ const char* mesh[] =
+ {
+ "../../data/box_high.ply",
+ "../../data/sphere.ply"
+ };
+
+ Vec3 lower = Vec3(4.0f, 1.0f, 0.0f);
+ float sizeInc = 0.0f;
+ float size = 1.0f;
+ int group = 0;
+
+ for (int i=0; i < 8; ++i)
+ {
+ CreateParticleShape(GetFilePathByPlatform(mesh[i%2]).c_str(), lower, size + i*sizeInc, rotation, spacing, Vec3(.0f, 0.0f, 0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.0f, 0.0f, g_params.radius*0.5f);
+
+ lower += Vec3(0.0f, size + i*sizeInc + 0.2f, 0.0f);
+ }
+
+ g_params.plasticThreshold = 0.00025f;
+ g_params.plasticCreep = 0.165f;
+
+ g_numSubsteps = 4;
+
+ // draw options
+ g_drawPoints = false;
+ }
+};
diff --git a/demo/scenes/player.h b/demo/scenes/player.h
new file mode 100644
index 0000000..83407ac
--- /dev/null
+++ b/demo/scenes/player.h
@@ -0,0 +1,290 @@
+
+
+/*
+class Player : public Scene
+{
+public:
+
+ Player(const char* filename) : Scene("Player"), mFilename(filename), mRecording(NULL)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ if (!mRecording)
+ mRecording = fopen(mFilename, "rb");
+
+ if (mRecording)
+ fseek(mRecording, 0, SEEK_SET);
+
+ // read first frame
+ ReadFrame();
+
+ g_lightDistance = 100.0f;
+ g_fogDistance = 0.0f;
+
+ g_camSpeed *= 100.0f;
+ g_camNear *= 100.0f;
+ g_camFar *= 100.0f;
+ g_pause = true;
+
+ g_dt = 1.0f/30.0f;
+ g_numSubsteps = 2;
+
+ g_drawPoints = true;
+
+ mInitialActive = g_buffers->activeIndices;
+ }
+
+ virtual void PostInitialize()
+ {
+ g_buffers->activeIndices = mInitialActive;
+
+ NvFlexSetActive(g_flex, &mInitialActive[0], mInitialActive.size(), eFlexMemoryHost);
+ }
+
+ virtual Matrix44 GetBasis()
+ {
+ // Coordinate fip for Unreal captures
+
+ Matrix44 flip = Matrix44::kIdentity;
+ flip.SetCol(1, Vec4(0.0f, 0.0f, -1.0f, 0.0f));
+ flip.SetCol(2, Vec4(0.0f, 1.0f, 0.0f, 0.0f));
+
+ return flip;
+ }
+
+ template<typename Element>
+ void ReadArray(std::vector<Element>& dest, bool enable=true)
+ {
+ if (feof(mRecording))
+ return;
+
+ int length;
+ int r;
+ r = fread(&length, sizeof(int), 1, mRecording);
+
+ if (feof(mRecording))
+ return;
+
+ if (enable)
+ {
+ int numElements = length/sizeof(Element);
+
+ dest.resize(numElements);
+ r = fread(&dest[0], length, 1, mRecording);
+ }
+ else
+ r = fseek(mRecording, length, SEEK_CUR);
+
+ (void)r;
+ }
+
+ virtual void KeyDown(int key)
+ {
+ if (key == '[')
+ {
+ Vec3 lower(FLT_MAX), upper(-FLT_MAX);
+
+ // particle bounds
+ for (int i=0; i < int(g_buffers->activeIndices.size()); ++i)
+ {
+ int index = g_buffers->activeIndices[i];
+
+ lower = Min(Vec3(g_buffers->positions[index]), lower);
+ upper = Max(Vec3(g_buffers->positions[index]), upper);
+ }
+
+ // center camera
+ g_camPos = (lower+upper)*0.5f;
+ g_camPos = GetBasis()*g_camPos;
+ }
+ }
+
+ bool VerifyArray(float* ptr, int n)
+ {
+ for (int i=0; i < n; ++i)
+ if (!isfinite(ptr[i]))
+ return false;
+
+ return true;
+ }
+
+ template <typename Element>
+ void ReadValue(Element& e, bool enable=true)
+ {
+ if (feof(mRecording))
+ return;
+
+ int r;
+ if (enable)
+ r = fread(&e, sizeof(e), 1, mRecording);
+ else
+ r = fseek(mRecording, sizeof(e), SEEK_CUR);
+
+ (void)r;
+ }
+
+ void ReadFrame()
+ {
+ if (!mRecording)
+ return;
+
+ if (feof(mRecording))
+ return;
+
+ // params
+ ReadValue(g_params, true);
+
+ // particle data
+ //ReadArray(g_buffers->positions, true);
+
+ if (true)
+ {
+ for (int i=0; i < int(g_buffers->positions.size()); ++i)
+ {
+ if (!isfinite(g_buffers->positions[i].x) ||
+ !isfinite(g_buffers->positions[i].y) ||
+ !isfinite(g_buffers->positions[i].z))
+ printf("particles failed at frame %d\n", g_frame);
+ }
+ }
+
+ ReadArray(g_buffers->restPositions, true);
+ ReadArray(g_buffers->velocities, true);
+ ReadArray(g_buffers->phases, true);
+ ReadArray(g_buffers->activeIndices, true);
+
+ // spring data
+ ReadArray(g_buffers->springIndices, true);
+ ReadArray(g_buffers->springLengths, true);
+ ReadArray(g_buffers->springStiffness, true);
+
+ // shape data
+ ReadArray(g_buffers->rigidIndices, true);
+ ReadArray(g_buffers->rigidLocalPositions, true);
+ ReadArray(g_buffers->rigidLocalNormals, true);
+
+ ReadArray(g_buffers->rigidCoefficients, true);
+ ReadArray(g_buffers->rigidOffsets, true);
+ ReadArray(g_buffers->rigidRotations, true);
+ ReadArray(g_buffers->rigidTranslations, true);
+
+ if (true)
+ {
+
+ if (!VerifyArray((float*)&g_buffers->rigidLocalPositions[0], g_buffers->rigidLocalPositions.size()*3))
+ printf("rigid local pos failed\n");
+
+ if (!VerifyArray((float*)&g_buffers->rigidTranslations[0], g_buffers->rigidTranslations.size()*3))
+ printf("rigid translations failed\n");
+
+ if (!VerifyArray((float*)&g_buffers->rigidRotations[0], g_buffers->rigidRotations.size()*3))
+ printf("rigid rotations failed\n");
+ }
+
+ // triangle data
+ ReadArray(g_buffers->triangles, true);
+ ReadArray(g_buffers->triangleNormals, true);
+
+ // convex shapes
+ ReadArray(g_buffers->shapeGeometry, true);
+ ReadArray(g_buffers->shapeAabbMin, true);
+ ReadArray(g_buffers->shapeAabbMax, true);
+ ReadArray(g_buffers->shapeStarts, true);
+ ReadArray(g_buffers->shapePositions, true);
+ ReadArray(g_buffers->shapeRotations, true);
+ ReadArray(g_buffers->shapePrevPositions, true);
+ ReadArray(g_buffers->shapePrevRotations, true);
+ ReadArray(g_buffers->shapeFlags, true);
+
+ if (true)
+ {
+ if (!VerifyArray((float*)&g_buffers->shapePositions[0], g_buffers->shapePositions.size()*4))
+ printf("shapes translations invalid\n");
+
+ if (!VerifyArray((float*)&g_buffers->shapeRotations[0], g_buffers->shapeRotations.size()*4))
+ printf("shapes rotations invalid\n");
+ }
+
+ int numMeshes = 0;
+ ReadValue(numMeshes);
+
+ // map serialized mesh ptrs to current meshes
+ std::map<NvFlexTriangleMeshId, NvFlexTriangleMeshId> originalToNewMeshMap;
+
+ for (int i=0; i < numMeshes; ++i)
+ {
+ Mesh m;
+
+ NvFlexTriangleMeshId originalPtr;
+ ReadValue(originalPtr);
+
+ ReadArray(m.m_positions);
+ ReadArray(m.m_indices);
+
+ if (!VerifyArray((float*)&m.m_positions[0], m.m_positions.size()*3))
+ printf("mesh vertices invalid\n");
+
+ printf("Creating mesh: %d faces %d vertices\n", m.GetNumFaces(), m.GetNumVertices());
+
+ Vec3 lower, upper;
+ m.GetBounds(lower, upper);
+ m.CalculateNormals();
+
+ NvFlexTriangleMeshId collisionMesh = NvFlexCreateTriangleMesh();
+ NvFlexUpdateTriangleMesh(collisionMesh, (float*)&m.m_positions[0], (int*)&m.m_indices[0], int(m.m_positions.size()), int(m.m_indices.size())/3, lower, upper, eFlexMemoryHost);
+
+ // create a render mesh
+ g_meshes[collisionMesh] = CreateGpuMesh(&m);
+
+ // create map from captured triangle mesh pointer to our recreated version
+ originalToNewMeshMap[originalPtr] = collisionMesh;
+ }
+
+ int numTriMeshInstances = 0;
+
+ // remap shape ptrs
+ for (int i=0; i < int(g_buffers->shapeFlags.size()); ++i)
+ {
+ if ((g_buffers->shapeFlags[i]&eNvFlexShapeFlagTypeMask) == eNvFlexShapeTriangleMesh)
+ {
+ numTriMeshInstances++;
+
+ NvFlexCollisionGeometry geo = g_buffers->shapeGeometry[g_buffers->shapeStarts[i]];
+
+ if (originalToNewMeshMap.find(geo.triMesh.mesh) == originalToNewMeshMap.end())
+ {
+ printf("Missing mesh for geometry entry\n");
+ assert(0);
+ }
+ else
+ {
+ g_buffers->shapeGeometry[g_buffers->shapeStarts[i]].mTriMesh.mMesh = originalToNewMeshMap[geo.triMesh.mesh];
+ }
+ }
+ }
+
+ printf("Num Tri Meshes: %d Num Tri Mesh Instances: %d\n", int(g_meshes.size()), numTriMeshInstances);
+
+ }
+
+ virtual void Draw(int pass)
+ {
+ }
+
+ virtual void DoGui()
+ {
+ }
+
+ virtual void Update()
+ {
+
+ }
+
+ const char* mFilename;
+ FILE* mRecording;
+
+ std::vector<int> mInitialActive;
+};
+*/
diff --git a/demo/scenes/potpourri.h b/demo/scenes/potpourri.h
new file mode 100644
index 0000000..338a09a
--- /dev/null
+++ b/demo/scenes/potpourri.h
@@ -0,0 +1,117 @@
+
+class PotPourri : public Scene
+{
+public:
+
+ PotPourri(const char* name) : Scene(name)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ int sx = 2;
+ int sy = 2;
+ int sz = 2;
+
+ Vec3 lower(0.0f, 4.2f + g_params.radius*0.25f, 0.0f);
+
+ int dimx = 5;
+ int dimy = 10;
+ int dimz = 5;
+
+ float radius = g_params.radius;
+ int group = 0;
+
+ if (1)
+ {
+ // create a basic grid
+ for (int y=0; y < dimy; ++y)
+ for (int z=0; z < dimz; ++z)
+ for (int x=0; x < dimx; ++x)
+ CreateParticleShape(
+ GetFilePathByPlatform("../../data/box.ply").c_str(),
+ (g_params.radius*0.905f)*Vec3(float(x*sx), float(y*sy), float(z*sz)) + (g_params.radius*0.1f)*Vec3(float(x),float(y),float(z)) + lower,
+ g_params.radius*0.9f*Vec3(float(sx), float(sy), float(sz)), 0.0f, g_params.radius*0.9f, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(group++, 0), true, 0.001f);
+
+ AddPlinth();
+ }
+
+ if (1)
+ {
+ int dimx = 60;
+ int dimy = 40;
+
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.5f;
+ float shearStiffness = 0.7f;
+
+ int clothStart = g_buffers->positions.size();
+
+ CreateSpringGrid(Vec3(0.0f, 0.0f, -1.0f), dimx, dimy, 1, radius*0.5f, NvFlexMakePhase(group++, 0), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), 1.0f);
+
+ int corner0 = clothStart + 0;
+ int corner1 = clothStart + dimx-1;
+ int corner2 = clothStart + dimx*(dimy-1);
+ int corner3 = clothStart + dimx*dimy-1;
+
+ g_buffers->positions[corner0].w = 0.0f;
+ g_buffers->positions[corner1].w = 0.0f;
+ g_buffers->positions[corner2].w = 0.0f;
+ g_buffers->positions[corner3].w = 0.0f;
+
+ // add tethers
+ for (int i=clothStart; i < int(g_buffers->positions.size()); ++i)
+ {
+ float x = g_buffers->positions[i].x;
+ g_buffers->positions[i].y = 4.0f - sinf(DegToRad(15.0f))*x;
+ g_buffers->positions[i].x = cosf(DegToRad(25.0f))*x;
+
+ if (i != corner0 && i != corner1 && i != corner2 && i != corner3)
+ {
+ float stiffness = -0.5f;
+ float give = 0.05f;
+
+ CreateSpring(corner0, i, stiffness, give);
+ CreateSpring(corner1, i, stiffness, give);
+ CreateSpring(corner2, i, stiffness, give);
+ CreateSpring(corner3, i, stiffness, give);
+ }
+ }
+
+ g_buffers->positions[corner1] = g_buffers->positions[corner0] + (g_buffers->positions[corner1]-g_buffers->positions[corner0])*0.9f;
+ g_buffers->positions[corner2] = g_buffers->positions[corner0] + (g_buffers->positions[corner2]-g_buffers->positions[corner0])*0.9f;
+ g_buffers->positions[corner3] = g_buffers->positions[corner0] + (g_buffers->positions[corner3]-g_buffers->positions[corner0])*0.9f;
+ }
+
+
+ for (int i=0; i < 50; ++i)
+ CreateParticleShape(GetFilePathByPlatform("../../data/banana.obj").c_str(), Vec3(0.4f, 8.5f + i*0.25f, 0.25f) + RandomUnitVector()*radius*0.25f, Vec3(1), 0.0f, radius, Vec3(0.0f), 1.0f, true, 0.5f, NvFlexMakePhase(group++, 0), true, radius*0.1f, 0.0f, 0.0f, 1.25f*Vec4(0.875f, 0.782f, 0.051f, 1.0f));
+
+
+ //g_numExtraParticles = 32*1024;
+ g_numSubsteps = 2;
+ g_params.numIterations = 4;
+
+ g_params.radius *= 1.0f;
+ g_params.staticFriction = 0.7f;
+ g_params.dynamicFriction = 0.75f;
+ g_params.dissipation = 0.01f;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.sleepThreshold = g_params.radius*0.25f;
+ g_params.damping = 0.25f;
+ g_params.maxAcceleration = 400.0f;
+
+ g_windStrength = 0.0f;
+
+ // draw options
+ g_drawPoints = false;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.radius*2.0f/g_dt);
+ }
+
+ virtual void Update()
+ {
+
+ }
+};
diff --git a/demo/scenes/rayleightaylor.h b/demo/scenes/rayleightaylor.h
new file mode 100644
index 0000000..f07fb46
--- /dev/null
+++ b/demo/scenes/rayleightaylor.h
@@ -0,0 +1,143 @@
+
+class RayleighTaylor3D : public Scene
+{
+public:
+
+ RayleighTaylor3D(const char* name) : Scene(name) {}
+
+ int base;
+ int width;
+ int height;
+ int depth;
+
+ virtual void Initialize()
+ {
+ float radius = 0.05f;
+ float restDistance = radius*0.5f;
+
+ width = 128;
+ height = 24;
+ depth = 24;
+
+ base = 4;
+
+ float sep = restDistance*0.9f;
+ int group = 0;
+
+ CreateParticleGrid(Vec3(0.0f, 0.0f, 0.0f), width, base, depth, sep, Vec3(0.0f), 0.0f, false, 0.0f, NvFlexMakePhase(group++, 0), 0.0f);
+ CreateParticleGrid(Vec3(0.0f, base*sep, 0.0f), width, height, depth, sep, Vec3(0.0f), 0.24f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+ CreateParticleGrid(Vec3(0.0f, sep*height + base*sep, 0.0f), width, height, depth, sep, Vec3(0.0f), 0.25f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+
+ g_params.gravity[1] = -9.f;
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.00f;
+ g_params.fluid = true;
+ g_params.viscosity = 2.0f;
+ g_params.numIterations = 10;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.anisotropyScale = 50.0f;
+ g_params.smoothing = 1.f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.numPlanes = 5;
+ g_params.cohesion = 0.0002125f;
+ g_params.surfaceTension = 0.0f;
+ g_params.collisionDistance = 0.001f;//restDistance*0.5f;
+ //g_params.solidPressure = 0.2f;
+
+ g_params.relaxationFactor = 20.0f;
+ g_numSubsteps = 5;
+
+ g_fluidColor = Vec4(0.2f, 0.6f, 0.9f, 1.0f);
+
+ g_lightDistance *= 0.85f;
+
+ // draw options
+ g_drawDensity = true;
+ g_drawDiffuse = false;
+ g_drawEllipsoids = false;
+ g_drawPoints = true;
+
+ g_blur = 2.0f;
+
+ g_warmup = true;
+ }
+
+ virtual void Update()
+ {
+ if (g_params.numPlanes == 4)
+ g_params.dynamicFriction = 0.2f;
+
+ if (g_frame == 32)
+ {
+ int layer1start = width*depth*base;
+ int layer1end = layer1start + width*height*depth;
+ for (int i = layer1start; i < layer1end; ++i)
+ g_buffers->positions[i].w = 1.0f;
+ }
+ }
+};
+
+class RayleighTaylor2D : public Scene
+{
+public:
+
+ RayleighTaylor2D(const char* name) : Scene(name) {}
+
+ int base;
+ int width;
+ int height;
+ int depth;
+
+ virtual void Initialize()
+ {
+ float radius = 0.05f;
+ float restDistance = radius*0.5f;
+
+ width = 128;
+ height = 24;
+ depth = 1;
+
+ base = 4;
+
+ float sep = restDistance*0.7f;
+ int group = 0;
+
+ CreateParticleGrid(Vec3(0.0f, 0.0f, 0.0f), width, base, depth, sep, Vec3(0.0f), 0.0f, false, 0.0f, NvFlexMakePhase(group++, 0), 0.0f);
+ CreateParticleGrid(Vec3(0.0f, base*sep, 0.0f), width, height, depth, sep, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+ CreateParticleGrid(Vec3(0.0f, sep*height + base*sep, 0.0f), width, height, depth, sep, Vec3(0.0f), 0.25f, false, 0.0f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), restDistance*0.01f);
+
+ g_params.gravity[1] = -9.f;
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.00f;
+ g_params.fluid = true;
+ g_params.viscosity = 0.0f;
+ g_params.numIterations = 10;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.anisotropyScale = 50.0f;
+ g_params.smoothing = 1.f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.numPlanes = 5;
+ g_params.cohesion = 0.0025f;
+ g_params.surfaceTension = 0.0f;
+ g_params.collisionDistance = 0.001f;
+ g_params.restitution = 0.0f;
+
+ g_params.relaxationFactor = 1.0f;
+ g_numSubsteps = 10;
+
+ g_fluidColor = Vec4(0.2f, 0.6f, 0.9f, 1.0f);
+
+ g_lightDistance *= 0.85f;
+
+ // draw options
+ g_drawDensity = false;
+ g_drawDiffuse = false;
+ g_drawEllipsoids = false;
+ g_drawPoints = true;
+
+ g_pointScale = 0.9f;
+ g_blur = 2.0f;
+
+ g_warmup = true;
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/restitution.h b/demo/scenes/restitution.h
new file mode 100644
index 0000000..521f526
--- /dev/null
+++ b/demo/scenes/restitution.h
@@ -0,0 +1,27 @@
+
+
+
+class Restitution : public Scene
+{
+public:
+
+ Restitution(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float radius = 0.05f;
+ CreateParticleGrid(Vec3(0.0f, 1.0f, 0.0f), 1, 1, 1, radius, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), 0.0f);
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.025f;
+ g_params.dissipation = 0.0f;
+ g_params.restitution = 1.0;
+ g_params.numIterations = 4;
+
+ g_numSubsteps = 4;
+
+ // draw options
+ g_drawPoints = true;
+ }
+
+};
diff --git a/demo/scenes/ridigbody.h b/demo/scenes/ridigbody.h
new file mode 100644
index 0000000..a6fd7c0
--- /dev/null
+++ b/demo/scenes/ridigbody.h
@@ -0,0 +1,139 @@
+class RigidBody : public Scene
+{
+public:
+
+ RigidBody(const char* name, const char* mesh) :
+ Scene(name),
+ mFile(mesh),
+ mScale(2.0f),
+ mOffset(0.0f, 1.0f, 0.0f),
+ mRadius(0.1f)
+ {
+ mStack[0] = 1;
+ mStack[1] = 1;
+ mStack[2] = 1;
+ }
+
+ virtual void Initialize()
+ {
+ float radius = mRadius;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.35f;
+ g_params.particleFriction = 0.25f;
+ g_params.numIterations = 4;
+ g_params.collisionDistance = radius*0.75f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = true;
+ g_wireframe = false;
+ g_drawSprings = false;
+ g_drawBases = true;
+
+ g_buffers->rigidOffsets.push_back(0);
+
+ mInstances.resize(0);
+
+
+ CreateBodies();
+
+ // fix any particles below the ground plane in place
+ for (int i = 0; i < int(g_buffers->positions.size()); ++i)
+ if (g_buffers->positions[i].y < 0.0f)
+ g_buffers->positions[i].w = 0.0f;
+
+ // expand radius for better self collision
+ g_params.radius *= 1.5f;
+
+ g_lightDistance *= 1.5f;
+ }
+
+ virtual void CreateBodies()
+ {
+ // build hard body
+ for (int x = 0; x < mStack[0]; ++x)
+ {
+ for (int y = 0; y < mStack[1]; ++y)
+ {
+ for (int z = 0; z < mStack[2]; ++z)
+ {
+ CreateRigidBody(mRadius, mOffset + Vec3(x*(mScale.x + 1), y*(mScale.y + 1), z*(mScale.z + 1))*mRadius);
+ }
+ }
+ }
+ }
+
+ void CreateRigidBody(float radius, Vec3 position, int group = 0)
+ {
+ Instance instance;
+
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform(mFile).c_str());
+ mesh->Normalize();
+ mesh->Transform(TranslationMatrix(Point3(position))*ScaleMatrix(mScale*radius));
+
+ instance.mMesh = mesh;
+ instance.mColor = Vec3(0.5f, 0.5f, 1.0f);
+
+ double createStart = GetSeconds();
+
+ //const float spacing = radius;
+ const float spacing = radius*0.5f;
+
+ NvFlexExtAsset* asset = NvFlexExtCreateRigidFromMesh(
+ (float*)&mesh->m_positions[0],
+ int(mesh->m_positions.size()),
+ (int*)&mesh->m_indices[0],
+ mesh->m_indices.size(),
+ spacing,
+ -spacing*0.5f);
+
+ double createEnd = GetSeconds();
+
+ const int particleOffset = g_buffers->positions.size();
+ const int indexOffset = g_buffers->rigidOffsets.back();
+
+ // add particle data to solver
+ for (int i = 0; i < asset->numParticles; ++i)
+ {
+ g_buffers->positions.push_back(&asset->particles[i * 4]);
+ g_buffers->velocities.push_back(0.0f);
+
+ const int phase = NvFlexMakePhase(group, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+ g_buffers->phases.push_back(phase);
+ }
+
+ // add shape data to solver
+ for (int i = 0; i < asset->numShapeIndices; ++i)
+ g_buffers->rigidIndices.push_back(asset->shapeIndices[i] + particleOffset);
+
+ for (int i = 0; i < asset->numShapes; ++i)
+ {
+ g_buffers->rigidOffsets.push_back(asset->shapeOffsets[i] + indexOffset);
+ g_buffers->rigidTranslations.push_back(Vec3(&asset->shapeCenters[i * 3]));
+ g_buffers->rigidRotations.push_back(Quat());
+ g_buffers->rigidCoefficients.push_back(asset->shapeCoefficients[i]);
+ }
+
+ NvFlexExtDestroyAsset(asset);
+
+ mInstances.push_back(instance);
+ }
+
+ struct Instance
+ {
+ Mesh* mMesh;
+ Vec3 mColor;
+ };
+
+ std::vector<Instance> mInstances;
+
+ const char* mFile;
+ Vec3 mScale;
+ Vec3 mOffset;
+
+ float mRadius;
+
+ int mStack[3];
+}; \ No newline at end of file
diff --git a/demo/scenes/rigidfluidcoupling.h b/demo/scenes/rigidfluidcoupling.h
new file mode 100644
index 0000000..b978eb8
--- /dev/null
+++ b/demo/scenes/rigidfluidcoupling.h
@@ -0,0 +1,61 @@
+
+class RigidFluidCoupling : public Scene
+{
+public:
+
+ RigidFluidCoupling(const char* name) : Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float minSize = 0.5f;
+ float maxSize = 1.0f;
+
+ float radius = 0.1f;
+ int group = 0;
+
+ Randf();
+
+ for (int i=0; i < 5; i++)
+ AddRandomConvex(10, Vec3(i*2.0f, 0.0f, Randf(0.0f, 2.0f)), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi*10.0f));
+
+ for (int z=0; z < 10; ++z)
+ for (int x=0; x < 50; ++x)
+ CreateParticleShape(
+ GetFilePathByPlatform("../../data/box.ply").c_str(),
+ Vec3(x*radius*2 - 1.0f, 1.0f + radius, 1.f + z*2.0f*radius) + 0.5f*Vec3(Randf(radius), 0.0f, Randf(radius)),
+ Vec3(2.0f, 2.0f + Randf(0.0f, 4.0f), 2.0f)*radius*0.5f,
+ 0.0f,
+ radius*0.5f,
+ Vec3(0.0f),
+ 1.0f,
+ true,
+ 1.0f,
+ NvFlexMakePhase(group++, 0),
+ true,
+ 0.0f);
+
+
+ // separte solid particle count
+ g_numSolidParticles = g_buffers->positions.size();
+
+ // number of fluid particles to allocate
+ g_numExtraParticles = 64*1024;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.5f;
+ g_params.fluid = true;
+ g_params.viscosity = 0.1f;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.anisotropyScale = 25.0f;
+ g_params.fluidRestDistance = g_params.radius*0.55f;
+
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = 2.0f*(g_params.fluidRestDistance)/g_dt;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/rigidpile.h b/demo/scenes/rigidpile.h
new file mode 100644
index 0000000..dcd1282
--- /dev/null
+++ b/demo/scenes/rigidpile.h
@@ -0,0 +1,129 @@
+
+class RigidPile : public Scene
+{
+public:
+
+ RigidPile(const char* name, int brickHeight) : Scene(name), mHeight(brickHeight)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ int sx = 2;
+ int sy = mHeight;
+ int sz = 2;
+
+ Vec3 lower(0.0f, 1.5f + g_params.radius*0.25f, 0.0f);
+
+ int dimx = 10;
+ int dimy = 10;
+ int dimz = 10;
+
+ float radius = g_params.radius;
+
+ if (1)
+ {
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform("../../data/box.ply").c_str());
+
+ // create a basic grid
+ for (int y=0; y < dimy; ++y)
+ for (int z=0; z < dimz; ++z)
+ for (int x=0; x < dimx; ++x)
+ CreateParticleShape(
+ mesh,
+ (g_params.radius*0.905f)*Vec3(float(x*sx), float(y*sy), float(z*sz)) + (g_params.radius*0.1f)*Vec3(float(x),float(y),float(z)) + lower,
+ g_params.radius*0.9f*Vec3(float(sx), float(sy), float(sz)), 0.0f, g_params.radius*0.9f, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(g_buffers->rigidOffsets.size()+1, 0), true, 0.002f);// 0.002f);
+
+
+ delete mesh;
+
+ AddPlinth();
+ }
+ else
+ {
+ // brick work
+ int wdimx = 10;
+ int wdimy = 10;
+
+ int bdimx = 4;
+ int bdimy = 2;
+ int bdimz = 2;
+
+ for (int y=0; y < wdimy; ++y)
+ {
+ for (int x=0; x < wdimx; ++x)
+ {
+ Vec3 lower = Vec3(x*bdimx*radius + 0.5f*radius, 0.93f*bdimy*y*radius, 0.0f);
+
+ if (y&1)
+ lower += Vec3(bdimx*0.25f*radius + 0.5f*radius, 0.0f, 0.0f);
+
+ //CreateParticleGrid(lower, bdimx, bdimy, bdimz, radius, Vec3(0.0f), 1.0f, true, g_buffers->rigidOffsets.size()+1, 0.0f);
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), lower + RandomUnitVector()*Vec3(0.0f, 0.0f, 0.0f), Vec3(bdimx*radius, bdimy*radius, bdimz*radius), 0.0f, radius, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(g_buffers->rigidOffsets.size()+1, 0), true, 0.0f, Vec3(0.0f, 0.0f, y*0.0001f));
+ }
+ }
+
+ if (0)
+ {
+ // create a basic grid
+ for (int y=0; y < dimy; ++y)
+ for (int z=0; z < 1; ++z)
+ for (int x=0; x < 1; ++x)
+ CreateParticleShape(
+ GetFilePathByPlatform("../../data/box.ply").c_str(),
+ 0.99f*(g_params.radius)*Vec3(float(x*sx), float(y*sy), float(z*sz)),
+ g_params.radius*Vec3(float(sx), float(sy), float(sz)), 0.0f, g_params.radius, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(g_buffers->rigidOffsets.size()+1, 0), true, 0.0f);
+
+ // create a basic grid
+ for (int y=0; y < dimy; ++y)
+ for (int z=0; z < 1; ++z)
+ for (int x=0; x < 1; ++x)
+ CreateParticleShape(
+ GetFilePathByPlatform("../../data/box.ply").c_str(),
+ 0.99f*(g_params.radius)*Vec3(float(sx*2 + x*sx), float(y*sy), float(z*sz)),
+ g_params.radius*Vec3(float(sx), float(sy), float(sz)), 0.0f, g_params.radius, Vec3(0.0f), 1.0f, true, 1.0f, NvFlexMakePhase(g_buffers->rigidOffsets.size()+1, 0), true, 0.0f);
+ }
+
+ }
+
+
+ if (0)
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.5f;
+ float shearStiffness = 0.7f;
+
+ int dimx = 40;
+ int dimy = 40;
+
+ CreateSpringGrid(Vec3(-1.0f, 1.0f + g_params.radius*0.5f, -1.0f), dimx, dimy, 1, g_params.radius*0.9f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), stretchStiffness, bendStiffness, shearStiffness, Vec3(0.0f), 1.0f);
+ }
+
+
+ //g_numExtraParticles = 32*1024;
+ g_numSubsteps = 2;
+ g_params.numIterations = 8;
+
+ g_params.radius *= 1.0f;
+ g_params.dynamicFriction = 0.4f;
+ g_params.dissipation = 0.01f;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.sleepThreshold = g_params.radius*0.25f;
+ g_params.shockPropagation = 3.f;
+
+ g_windStrength = 0.0f;
+
+ // draw options
+ g_drawPoints = false;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.radius*2.0f/g_dt);
+ }
+
+ virtual void Update()
+ {
+
+ }
+
+ int mHeight;
+};
diff --git a/demo/scenes/rigidrotation.h b/demo/scenes/rigidrotation.h
new file mode 100644
index 0000000..8abcb98
--- /dev/null
+++ b/demo/scenes/rigidrotation.h
@@ -0,0 +1,56 @@
+
+
+class RigidRotation : public Scene
+{
+public:
+
+ RigidRotation(const char* name) : Scene(name)
+ {
+ }
+
+ void Initialize()
+ {
+ float radius = 0.1f;
+
+ float dimx = 1.0f;
+ float dimy = 5.0f;
+ float dimz = 1.0f;
+
+ CreateParticleShape(GetFilePathByPlatform("../../data/box.ply").c_str(), Vec3(0.0f, 1.0f, 0.0f), Vec3(dimx, dimy, dimz)*radius, 0.0f, radius, Vec3(0.0f), 1.0f, true, 1.0f, 0, true, 0.0f, 0.0f, 0.0f);
+
+ g_params.radius = radius;
+ g_params.gravity[1] = 0;
+
+ g_params.numIterations = 1;
+ g_numSubsteps = 1;
+
+ g_pause = true;
+
+ g_drawBases = true;
+ }
+
+ void Update()
+ {
+ if (g_frame == 0)
+ {
+ // rotate particles by 90 degrees
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ Vec3 center = (lower + upper)*0.5f;
+
+ Matrix44 rotation = RotationMatrix(DegToRad(95.0f), Vec3(0.0f, 0.0f, 1.0f));
+
+ for (int i = 0; i < int(g_buffers->positions.size()); ++i)
+ {
+ Vec3 delta = Vec3(g_buffers->positions[i]) - center;
+
+ delta = Vec3(rotation*Vec4(delta, 1.0f));
+
+ g_buffers->positions[i].x = center.x + delta.x;
+ g_buffers->positions[i].y = center.y + delta.y;
+ g_buffers->positions[i].z = center.z + delta.z;
+ }
+ }
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/rockpool.h b/demo/scenes/rockpool.h
new file mode 100644
index 0000000..e7c0034
--- /dev/null
+++ b/demo/scenes/rockpool.h
@@ -0,0 +1,78 @@
+
+
+
+class RockPool: public Scene
+{
+public:
+
+ RockPool(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float radius = 0.1f;
+
+ // convex rocks
+ float minSize = 0.1f;
+ float maxSize = 0.5f;
+
+ for (int i=0; i < 4; i++)
+ for (int j=0; j < 2; j++)
+ AddRandomConvex(10, Vec3(48*radius*0.5f + i*maxSize*2.0f, 0.0f, j*maxSize*2.0f), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi));
+
+ CreateParticleGrid(Vec3(0.0f, radius*0.5f, -1.0f), 32, 32, 32, radius*0.55f, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid), 0.005f);
+
+ g_numSubsteps = 2;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.00f;
+ g_params.fluid = true;
+ g_params.viscosity = 0.01f;
+ g_params.numIterations = 2;
+ g_params.vorticityConfinement = 75.0f;
+ g_params.anisotropyScale = 30.0f;
+ g_params.fluidRestDistance = radius*0.6f;
+ g_params.relaxationFactor = 1.0f;
+ g_params.smoothing = 0.5f;
+ g_params.diffuseThreshold *= 0.25f;
+ g_params.cohesion = 0.05f;
+
+ g_maxDiffuseParticles = 64*1024;
+ g_diffuseScale = 0.5f;
+ g_params.diffuseBallistic = 16;
+ g_params.diffuseBuoyancy = 1.0f;
+ g_params.diffuseDrag = 1.0f;
+
+ g_emitters[0].mEnabled = false;
+
+ g_params.numPlanes = 5;
+
+ g_waveFloorTilt = 0.0f;
+ g_waveFrequency = 1.5f;
+ g_waveAmplitude = 2.0f;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ g_drawDiffuse = true;
+ g_lightDistance = 1.8f;
+
+ g_numExtraParticles = 80*1024;
+
+ Emitter e1;
+ e1.mDir = Vec3(-1.0f, 0.0f, 0.0f);
+ e1.mRight = Vec3(0.0f, 0.0f, 1.0f);
+ e1.mPos = Vec3(3.8f, 1.f, 1.f) ;
+ e1.mSpeed = (g_params.fluidRestDistance/g_dt)*2.0f; // 2 particle layers per-frame
+ e1.mEnabled = true;
+
+ Emitter e2;
+ e2.mDir = Vec3(1.0f, 0.0f, 0.0f);
+ e2.mRight = Vec3(0.0f, 0.0f, -1.0f);
+ e2.mPos = Vec3(2.f, 1.f, -0.f);
+ e2.mSpeed = (g_params.fluidRestDistance/g_dt)*2.0f; // 2 particle layers per-frame
+ e2.mEnabled = true;
+
+ g_emitters.push_back(e1);
+ g_emitters.push_back(e2);
+ }
+};
diff --git a/demo/scenes/sdfcollision.h b/demo/scenes/sdfcollision.h
new file mode 100644
index 0000000..aab2182
--- /dev/null
+++ b/demo/scenes/sdfcollision.h
@@ -0,0 +1,54 @@
+
+class SDFCollision : public Scene
+{
+public:
+
+ SDFCollision(const char* name) : Scene(name)
+ {
+ }
+
+ virtual void Initialize()
+ {
+ const int dim = 128;
+
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/bunny.ply").c_str(), dim);
+
+ AddSDF(sdf, Vec3(-1.f, 0.0f, 0.0f), QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), DegToRad(-45.0f)), 0.5f);
+ AddSDF(sdf, Vec3(0.0f, 0.0f, 0.0f), QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), DegToRad(0.0f)), 1.0f);
+ AddSDF(sdf, Vec3(1.0f, 0.0f, 0.0f), QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), DegToRad(45.0f)), 2.0f);
+
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.8f;
+ float shearStiffness = 0.5f;
+
+ int dimx = 64;
+ int dimz = 64;
+ float radius = 0.05f;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+
+ CreateSpringGrid(Vec3(-0.6f, 2.9f, -0.6f), dimx, dimz, 1, radius*0.75f, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+
+ Vec3 lower, upper;
+ GetParticleBounds(lower, upper);
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.4f;
+ g_params.staticFriction = 0.4f;
+ g_params.particleFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.02f;
+ g_params.lift = 0.1f;
+ g_params.collisionDistance = radius*0.5f;
+ g_params.relaxationFactor = 1.3f;
+
+ g_numSubsteps = 3;
+
+ g_windStrength = 0.0f;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = false;
+ }
+};
diff --git a/demo/scenes/shapecollision.h b/demo/scenes/shapecollision.h
new file mode 100644
index 0000000..07409b5
--- /dev/null
+++ b/demo/scenes/shapecollision.h
@@ -0,0 +1,101 @@
+
+
+class ShapeCollision : public Scene
+{
+public:
+
+ ShapeCollision(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float maxShapeRadius = 0.25f;
+ float minShapeRadius = 0.1f;
+
+ int dimx = 4;
+ int dimy = 4;
+ int dimz = 4;
+
+ float radius = 0.05f;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide);
+
+ g_numSubsteps = 2;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.restitution = 0.0;
+ g_params.numIterations = 2;
+ g_params.particleCollisionMargin = g_params.radius*0.5f;
+ g_params.shapeCollisionMargin = g_params.radius*0.5f;
+ g_params.maxSpeed = 0.5f*radius*float(g_numSubsteps)/g_dt;
+
+ CreateParticleGrid(Vec3(0.0f, 1.0f + dimy*maxShapeRadius*2.0f, 0.0f), 30, 50, 30, radius, Vec3(0.0f), 1.0f, false, 0.0f, phase, 0.0f);
+
+ Mesh* box = ImportMesh(GetFilePathByPlatform("../../data/box.ply").c_str());
+ box->Normalize(1.0f);
+
+ NvFlexTriangleMeshId mesh = CreateTriangleMesh(box);
+ delete box;
+
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/bunny.ply").c_str(), 128);
+
+ for (int i=0; i < dimx; ++i)
+ {
+ for (int j=0; j < dimy; ++j)
+ {
+ for (int k=0; k < dimz; ++k)
+ {
+ int type = Rand()%6;
+
+ Vec3 shapeTranslation = Vec3(float(i),float(j) + 0.5f,float(k))*maxShapeRadius*2.0f;
+ Quat shapeRotation = QuatFromAxisAngle(UniformSampleSphere(), Randf()*k2Pi);
+
+ switch(type)
+ {
+ case 0:
+ {
+ AddSphere(Randf(minShapeRadius, maxShapeRadius), shapeTranslation, shapeRotation);
+ break;
+ }
+ case 1:
+ {
+ AddCapsule(Randf(minShapeRadius, maxShapeRadius)*0.5f, Randf()*maxShapeRadius, shapeTranslation, shapeRotation);
+ break;
+ }
+ case 2:
+ {
+ Vec3 extents = 0.75f*Vec3(Randf(minShapeRadius, maxShapeRadius),
+ Randf(minShapeRadius, maxShapeRadius),
+ Randf(minShapeRadius, maxShapeRadius));
+
+ AddBox(extents, shapeTranslation, shapeRotation);
+ break;
+ }
+ case 3:
+ {
+ AddRandomConvex(6 + Rand()%6, shapeTranslation, minShapeRadius, maxShapeRadius, UniformSampleSphere(), Randf()*k2Pi);
+ break;
+ }
+ case 4:
+ {
+ AddTriangleMesh(mesh, shapeTranslation, shapeRotation, Randf(0.5f, 1.0f)*maxShapeRadius);
+ break;
+ }
+ case 5:
+ {
+ AddSDF(sdf, shapeTranslation, shapeRotation, maxShapeRadius*2.0f);
+ break;
+ }
+ };
+ }
+ }
+ }
+ }
+
+ virtual void CenterCamera()
+ {
+ g_camPos.y = 2.0f;
+ g_camPos.z = 6.0f;
+ }
+
+}; \ No newline at end of file
diff --git a/demo/scenes/softbody.h b/demo/scenes/softbody.h
new file mode 100644
index 0000000..c12680c
--- /dev/null
+++ b/demo/scenes/softbody.h
@@ -0,0 +1,325 @@
+
+
+class SoftBody : public Scene
+{
+public:
+
+ SoftBody(const char* name, const char* mesh) :
+ Scene(name),
+ mFile(mesh),
+ mScale(2.0f),
+ mOffset(0.0f, 1.0f, 0.0f),
+ mRadius(0.1f),
+ mClusterSpacing(1.0f),
+ mClusterRadius(0.0f),
+ mClusterStiffness(0.5f),
+ mLinkRadius(0.0f),
+ mLinkStiffness(1.0f),
+ mGlobalStiffness(0.0f),
+ mSurfaceSampling(0.0f),
+ mVolumeSampling(4.0f),
+ mSkinningFalloff(2.0f),
+ mSkinningMaxDistance(100.0f),
+ mPlasticThreshold(0.0f),
+ mPlasticCreep(0.0f),
+ mRelaxationFactor(1.0f),
+ mPlinth(false)
+ {
+ mStack[0] = 1;
+ mStack[1] = 1;
+ mStack[2] = 1;
+ }
+
+ virtual void Initialize()
+ {
+ float radius = mRadius;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.35f;
+ g_params.particleFriction = 0.25f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 4;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.0f;
+ g_params.lift = 0.0f;
+ g_params.collisionDistance = radius*0.75f;
+
+ g_params.plasticThreshold = mPlasticThreshold;
+ g_params.plasticCreep = mPlasticCreep;
+
+ g_params.relaxationFactor = mRelaxationFactor;
+
+ g_windStrength = 0.0f;
+
+ g_numSubsteps = 2;
+
+ // draw options
+ g_drawPoints = false;
+ g_wireframe = false;
+ g_drawSprings = false;
+ g_drawBases = false;
+
+ g_buffers->rigidOffsets.push_back(0);
+
+ mInstances.resize(0);
+
+
+ CreateBodies();
+
+ if (mPlinth) {
+ AddPlinth();
+ }
+
+ // fix any particles below the ground plane in place
+ for (int i = 0; i < int(g_buffers->positions.size()); ++i)
+ if (g_buffers->positions[i].y < 0.0f)
+ g_buffers->positions[i].w = 0.0f;
+
+ // expand radius for better self collision
+ g_params.radius *= 1.5f;
+
+ g_lightDistance *= 1.5f;
+ }
+
+ virtual void CreateBodies()
+ {
+ // build soft body
+ for (int x = 0; x < mStack[0]; ++x)
+ {
+ for (int y = 0; y < mStack[1]; ++y)
+ {
+ for (int z = 0; z < mStack[2]; ++z)
+ {
+ CreateSoftBody(mRadius, mOffset + Vec3(x*(mScale.x + 1), y*(mScale.y + 1), z*(mScale.z + 1))*mRadius, mClusterStiffness, mInstances.size());
+ }
+ }
+ }
+ }
+
+ void CreateSoftBody(float radius, Vec3 position, float clusterStiffness, int group = 0)
+ {
+ Instance instance;
+
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform(mFile).c_str());
+ mesh->Normalize();
+ mesh->Transform(TranslationMatrix(Point3(position))*ScaleMatrix(mScale*radius));
+
+ instance.mMesh = mesh;
+ instance.mColor = Vec3(0.5f, 0.5f, 1.0f);
+ instance.mOffset = g_buffers->rigidTranslations.size();
+
+ double createStart = GetSeconds();
+
+ // create soft body definition
+ NvFlexExtAsset* asset = NvFlexExtCreateSoftFromMesh(
+ (float*)&instance.mMesh->m_positions[0],
+ instance.mMesh->m_positions.size(),
+ (int*)&instance.mMesh->m_indices[0],
+ instance.mMesh->m_indices.size(),
+ radius,
+ mVolumeSampling,
+ mSurfaceSampling,
+ mClusterSpacing*radius,
+ mClusterRadius*radius,
+ clusterStiffness,
+ mLinkRadius*radius,
+ mLinkStiffness,
+ mGlobalStiffness);
+
+ double createEnd = GetSeconds();
+
+ // create skinning
+ const int maxWeights = 4;
+
+ instance.mSkinningIndices.resize(instance.mMesh->m_positions.size()*maxWeights);
+ instance.mSkinningWeights.resize(instance.mMesh->m_positions.size()*maxWeights);
+
+ for (int i = 0; i < asset->numShapes; ++i)
+ instance.mRigidRestPoses.push_back(Vec3(&asset->shapeCenters[i * 3]));
+
+ double skinStart = GetSeconds();
+
+ NvFlexExtCreateSoftMeshSkinning(
+ (float*)&instance.mMesh->m_positions[0],
+ instance.mMesh->m_positions.size(),
+ asset->shapeCenters,
+ asset->numShapes,
+ mSkinningFalloff,
+ mSkinningMaxDistance,
+ &instance.mSkinningWeights[0],
+ &instance.mSkinningIndices[0]);
+
+ double skinEnd = GetSeconds();
+
+ printf("Created soft in %f ms Skinned in %f\n", (createEnd - createStart)*1000.0f, (skinEnd - skinStart)*1000.0f);
+
+ const int particleOffset = g_buffers->positions.size();
+ const int indexOffset = g_buffers->rigidOffsets.back();
+
+ // add particle data to solver
+ for (int i = 0; i < asset->numParticles; ++i)
+ {
+ g_buffers->positions.push_back(&asset->particles[i * 4]);
+ g_buffers->velocities.push_back(0.0f);
+
+ const int phase = NvFlexMakePhase(group, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+ g_buffers->phases.push_back(phase);
+ }
+
+ // add shape data to solver
+ for (int i = 0; i < asset->numShapeIndices; ++i)
+ g_buffers->rigidIndices.push_back(asset->shapeIndices[i] + particleOffset);
+
+ for (int i = 0; i < asset->numShapes; ++i)
+ {
+ g_buffers->rigidOffsets.push_back(asset->shapeOffsets[i] + indexOffset);
+ g_buffers->rigidTranslations.push_back(Vec3(&asset->shapeCenters[i * 3]));
+ g_buffers->rigidRotations.push_back(Quat());
+ g_buffers->rigidCoefficients.push_back(asset->shapeCoefficients[i]);
+ }
+
+ // add link data to the solver
+ for (int i = 0; i < asset->numSprings; ++i)
+ {
+ g_buffers->springIndices.push_back(asset->springIndices[i * 2 + 0]);
+ g_buffers->springIndices.push_back(asset->springIndices[i * 2 + 1]);
+
+ g_buffers->springStiffness.push_back(asset->springCoefficients[i]);
+ g_buffers->springLengths.push_back(asset->springRestLengths[i]);
+ }
+
+ NvFlexExtDestroyAsset(asset);
+
+ mInstances.push_back(instance);
+ }
+
+ virtual void Draw(int pass)
+ {
+ if (!g_drawMesh)
+ return;
+
+ for (int s = 0; s < int(mInstances.size()); ++s)
+ {
+ const Instance& instance = mInstances[s];
+
+ Mesh m;
+ m.m_positions.resize(instance.mMesh->m_positions.size());
+ m.m_normals.resize(instance.mMesh->m_normals.size());
+ m.m_indices = instance.mMesh->m_indices;
+
+ for (int i = 0; i < int(instance.mMesh->m_positions.size()); ++i)
+ {
+ Vec3 softPos;
+ Vec3 softNormal;
+
+ for (int w = 0; w < 4; ++w)
+ {
+ const int cluster = instance.mSkinningIndices[i * 4 + w];
+ const float weight = instance.mSkinningWeights[i * 4 + w];
+
+ if (cluster > -1)
+ {
+ // offset in the global constraint array
+ int rigidIndex = cluster + instance.mOffset;
+
+ Vec3 localPos = Vec3(instance.mMesh->m_positions[i]) - instance.mRigidRestPoses[cluster];
+
+ Vec3 skinnedPos = g_buffers->rigidTranslations[rigidIndex] + Rotate(g_buffers->rigidRotations[rigidIndex], localPos);
+ Vec3 skinnedNormal = Rotate(g_buffers->rigidRotations[rigidIndex], instance.mMesh->m_normals[i]);
+
+ softPos += skinnedPos*weight;
+ softNormal += skinnedNormal*weight;
+ }
+ }
+
+ m.m_positions[i] = Point3(softPos);
+ m.m_normals[i] = softNormal;
+ }
+
+ DrawMesh(&m, instance.mColor);
+ }
+ }
+
+ struct Instance
+ {
+ Mesh* mMesh;
+ std::vector<int> mSkinningIndices;
+ std::vector<float> mSkinningWeights;
+ vector<Vec3> mRigidRestPoses;
+ Vec3 mColor;
+ int mOffset;
+ };
+
+ std::vector<Instance> mInstances;
+
+ const char* mFile;
+ Vec3 mScale;
+ Vec3 mOffset;
+
+ float mRadius;
+
+ float mClusterSpacing;
+ float mClusterRadius;
+ float mClusterStiffness;
+
+ float mLinkRadius;
+ float mLinkStiffness;
+
+ float mGlobalStiffness;
+
+ float mSurfaceSampling;
+ float mVolumeSampling;
+
+ float mSkinningFalloff;
+ float mSkinningMaxDistance;
+
+ float mPlasticThreshold;
+ float mPlasticCreep;
+
+ float mRelaxationFactor;
+
+ bool mPlinth;
+
+ int mStack[3];
+};
+
+
+
+
+class SoftBodyFixed : public SoftBody
+{
+public:
+
+ SoftBodyFixed(const char* name, const char* mesh) : SoftBody(name, mesh) {}
+
+ virtual void Initialize()
+ {
+ SoftBody::Initialize();
+
+ // fix any particles in the wall
+ for (int i = 0; i < int(g_buffers->positions.size()); ++i)
+ if (g_buffers->positions[i].x < mRadius)
+ g_buffers->positions[i].w = 0.0f;
+ }
+
+ virtual void CreateBodies()
+ {
+ int x = 0;
+ int y = 0;
+
+ for (int z = 0; z < 4; ++z)
+ {
+ float stiffness = sqr(mClusterStiffness*(z + 1));
+
+ CreateSoftBody(mRadius, mOffset + Vec3(x*(mScale.x + 1), y*(mScale.y + 1), -z*(mScale.z + 1))*mRadius, stiffness, mInstances.size());
+ }
+ }
+
+ virtual void PostInitialize()
+ {
+ SoftBody::PostInitialize();
+
+ (Vec4&)g_params.planes[1] = Vec4(1.0f, 0.0f, 0.0f, 0.0f);
+ g_params.numPlanes = 2;
+ }
+};
diff --git a/demo/scenes/spherecloth.h b/demo/scenes/spherecloth.h
new file mode 100644
index 0000000..cab7f4d
--- /dev/null
+++ b/demo/scenes/spherecloth.h
@@ -0,0 +1,85 @@
+
+
+
+class SphereCloth : public Scene
+{
+public:
+
+ SphereCloth(const char* name) :
+ Scene(name) {}
+
+ virtual void Initialize()
+ {
+ float stretchStiffness = 1.0f;
+ float bendStiffness = 0.5f;
+ float shearStiffness = 0.5f;
+
+ float radius = 0.05f;
+
+ int dimx = 70;
+ int dimz = 70;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+
+ float spacing = radius*0.8f;
+
+ CreateSpringGrid(Vec3(-dimx*spacing*0.5f, 1.5f, -dimz*spacing*0.5f), dimx, dimz, 1, spacing, phase, stretchStiffness, bendStiffness, shearStiffness, 0.0f, 1.0f);
+
+ g_params.radius = radius*1.0f;
+ g_params.dynamicFriction = 0.45f;
+ g_params.particleFriction = 0.45f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 8;
+ g_params.viscosity = 0.0f;
+ g_params.drag = 0.05f;
+ g_params.collisionDistance = radius*0.5f;
+ g_params.relaxationMode = eNvFlexRelaxationGlobal;
+ g_params.relaxationFactor = 0.25f;
+ g_params.numPlanes = 1;
+
+ g_numSubsteps = 2;
+
+ g_windStrength = 0.0f;
+
+ // draw options
+ g_drawPoints = false;
+ g_drawSprings = false;
+
+ g_lightDistance *= 1.5f;
+
+ mTime = 0.0f;
+ }
+
+ void Update()
+ {
+ ClearShapes();
+
+ mTime += g_dt;
+
+ // let cloth settle on object
+ float startTime = 1.0f;
+
+ float time = Max(0.0f, mTime-startTime);
+ float lastTime = Max(0.0f, time-g_dt);
+
+ const float rotationSpeed = 1.0f;
+ const float translationSpeed = 0.0f;
+
+ Vec3 pos = Vec3(translationSpeed*(1.0f-cosf(time)), 0.5f, 0.0f);
+ Vec3 prevPos = Vec3(translationSpeed*(1.0f-cosf(lastTime)), 0.5f, 0.0f);
+
+ Quat rot = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), kPi*(1.0f-cosf(rotationSpeed*time)));
+ Quat prevRot = QuatFromAxisAngle(Vec3(0.0f, 1.0f, 0.0f), kPi*(1.0f-cosf(rotationSpeed*lastTime)));
+
+ AddSphere(0.5f, pos, rot);
+ //AddCapsule(0.25f, 0.5f, pos, rot);
+
+ g_buffers->shapePrevPositions[0] = Vec4(prevPos, 0.0f);
+ g_buffers->shapePrevRotations[0] = prevRot;
+
+ UpdateShapes();
+ }
+
+ float mTime;
+ int mType;
+};
+
diff --git a/demo/scenes/surfacetension.h b/demo/scenes/surfacetension.h
new file mode 100644
index 0000000..b22ad6d
--- /dev/null
+++ b/demo/scenes/surfacetension.h
@@ -0,0 +1,57 @@
+
+class SurfaceTension : public Scene
+{
+public:
+
+ SurfaceTension(const char* name, float surfaceTension) : Scene(name), surfaceTension(surfaceTension) {}
+
+ virtual void Initialize()
+ {
+ mCounter = 0;
+
+ float radius = 0.1f;
+ float restDistance = radius*0.55f;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid);
+
+ CreateParticleGrid(Vec3(0.0f, 0.2f, -1.0f), 16, 64, 16, restDistance, Vec3(0.0f), 1.0f, false, 0.0f, phase, 0.005f);
+
+ g_params.radius = radius;
+
+ g_params.fluid = true;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.anisotropyScale = 2.5f / radius;
+ g_params.smoothing = 0.5f;
+ g_params.relaxationFactor = 1.f;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = 0.01f;
+
+ g_params.dynamicFriction = 0.25f;
+ g_params.viscosity = 0.5f;
+ g_params.cohesion = 0.1f;
+ g_params.adhesion = 0.0f;
+ g_params.surfaceTension = surfaceTension;
+
+ g_params.gravity[1] = 0.0f;
+
+ g_numExtraParticles = 64 * 1024;
+
+ g_emitters[0].mEnabled = true;
+ g_emitters[0].mSpeed = (g_params.fluidRestDistance*2.f / g_dt);
+
+ g_lightDistance *= 2.0f;
+
+ // draw options
+ g_drawEllipsoids = true;
+ }
+
+ void Update()
+ {
+ if (g_frame == 300)
+ g_params.gravity[1] = -9.8f;
+ }
+
+ int mCounter;
+ float surfaceTension;
+};
diff --git a/demo/scenes/tearing.h b/demo/scenes/tearing.h
new file mode 100644
index 0000000..c36ca7a
--- /dev/null
+++ b/demo/scenes/tearing.h
@@ -0,0 +1,132 @@
+
+class Tearing : public Scene
+{
+public:
+
+ Tearing(const char* name) : Scene(name) {}
+
+ ~Tearing()
+ {
+ NvFlexExtDestroyTearingCloth(mCloth);
+ }
+
+ void Initialize()
+ {
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform("../../data/irregular_plane.obj").c_str());
+ mesh->Transform(RotationMatrix(kPi, Vec3(0.0f, 1.0f, 0.0f))*RotationMatrix(kPi*0.5f, Vec3(1.0f, 0.0f, 0.0f))*ScaleMatrix(2.0f));
+
+ Vec3 lower, upper;
+ mesh->GetBounds(lower, upper);
+
+ float radius = 0.065f;
+ int phase = NvFlexMakePhase(0, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter);
+
+ for (size_t i = 0; i < mesh->GetNumVertices(); ++i)
+ {
+ Vec3 p = Vec3(mesh->m_positions[i]);
+
+ float invMass = 1.0f;
+
+ if (p.y == upper.y)
+ invMass = 0.0f;
+
+ p += Vec3(0.0f, 1.5f, 0.0f);
+
+ g_buffers->positions.push_back(Vec4(p.x, p.y, p.z, invMass));
+ g_buffers->velocities.push_back(0.0f);
+ g_buffers->phases.push_back(phase);
+ }
+
+ g_numExtraParticles = 1000;
+
+ mCloth = NvFlexExtCreateTearingClothFromMesh((float*)&g_buffers->positions[0], int(g_buffers->positions.size()), int(g_buffers->positions.size()) + g_numExtraParticles, (int*)&mesh->m_indices[0], mesh->GetNumFaces(), 0.8f, 0.8f, 0.0f);
+
+ g_buffers->triangles.assign((int*)&mesh->m_indices[0], mesh->m_indices.size());
+ g_buffers->triangleNormals.resize(mesh->GetNumFaces(), Vec3(0.0f, 0.0f, 1.0f));
+
+ g_buffers->springIndices.assign(mCloth->springIndices, mCloth->numSprings * 2);
+ g_buffers->springStiffness.assign(mCloth->springCoefficients, mCloth->numSprings);
+ g_buffers->springLengths.assign(mCloth->springRestLengths, mCloth->numSprings);
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.025f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 16;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.relaxationFactor = 1.0f;
+ g_params.drag = 0.03f;
+
+ g_params.relaxationMode = eNvFlexRelaxationGlobal;
+ g_params.relaxationFactor = 0.35f;
+
+ g_numSubsteps = 2;
+
+ g_pause = false;
+
+ // draw options
+ g_drawPoints = false;
+ }
+
+ void Update()
+ {
+ g_params.wind[0] = 0.1f;
+ g_params.wind[1] = 0.1f;
+ g_params.wind[2] = -0.2f;
+ g_windStrength = 6.0f;
+
+ const float maxStrain = 3.0f;
+ const int maxCopies = 2048;
+ const int maxEdits = 2048;
+
+ NvFlexExtTearingParticleClone particleCopies[maxCopies];
+ int numParticleCopies;
+
+ NvFlexExtTearingMeshEdit triangleEdits[maxEdits];
+ int numTriangleEdits;
+
+ // update asset's copy of the particles
+ memcpy(mCloth->particles, &g_buffers->positions[0], sizeof(Vec4)*g_buffers->positions.size());
+
+ NvFlexExtTearClothMesh(mCloth, maxStrain, 4, particleCopies, &numParticleCopies, maxCopies, triangleEdits, &numTriangleEdits, maxEdits);
+
+ // copy particles
+ for (int i = 0; i < numParticleCopies; ++i)
+ {
+ const int srcIndex = particleCopies[i].srcIndex;
+ const int destIndex = particleCopies[i].destIndex;
+
+ g_buffers->positions[destIndex] = Vec4(Vec3(g_buffers->positions[srcIndex]), 1.0f); // override mass because picked particle has inf. mass
+ g_buffers->restPositions[destIndex] = g_buffers->restPositions[srcIndex];
+ g_buffers->velocities[destIndex] = g_buffers->velocities[srcIndex];
+ g_buffers->phases[destIndex] = g_buffers->phases[srcIndex];
+
+ g_buffers->activeIndices.push_back(destIndex);
+ }
+
+ // apply triangle modifications to index buffer
+ for (int i = 0; i < numTriangleEdits; ++i)
+ {
+ const int index = triangleEdits[i].triIndex;
+ const int newValue = triangleEdits[i].newParticleIndex;
+
+ g_buffers->triangles[index] = newValue;
+ }
+
+ mCloth->numParticles += numParticleCopies;
+
+ // update constraints
+ g_buffers->springIndices.assign(mCloth->springIndices, mCloth->numSprings * 2);
+ g_buffers->springStiffness.assign(mCloth->springCoefficients, mCloth->numSprings);
+ g_buffers->springLengths.assign(mCloth->springRestLengths, mCloth->numSprings);
+ }
+
+ virtual void Sync()
+ {
+ // update solver data not already updated in the main loop
+ NvFlexSetSprings(g_flex, g_buffers->springIndices.buffer, g_buffers->springLengths.buffer, g_buffers->springStiffness.buffer, g_buffers->springLengths.size());
+ NvFlexSetDynamicTriangles(g_flex, g_buffers->triangles.buffer, g_buffers->triangleNormals.buffer, g_buffers->triangles.size() / 3);
+ NvFlexSetRestParticles(g_flex, g_buffers->restPositions.buffer, g_buffers->restPositions.size());
+ }
+
+ NvFlexExtAsset* mCloth;
+}; \ No newline at end of file
diff --git a/demo/scenes/thinbox.h b/demo/scenes/thinbox.h
new file mode 100644
index 0000000..a77f258
--- /dev/null
+++ b/demo/scenes/thinbox.h
@@ -0,0 +1,66 @@
+
+
+class ThinBox : public Scene
+{
+public:
+
+ ThinBox(const char* name) : Scene(name) {}
+
+ int base;
+ int width;
+ int height;
+ int depth;
+
+ virtual void Initialize()
+ {
+ float radius = 0.03f;
+
+ width = 16;
+ height = 8;
+ depth = 8;
+
+ base = 4;
+
+ float sep = radius;
+
+ CreateParticleGrid(Vec3(0.0f, radius, 0.0f), width, height, depth, sep, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), 0.0f);
+
+ Vec3 upper;
+ Vec3 lower;
+ GetParticleBounds(lower, upper);
+ lower -= Vec3(radius*0.5f);
+ upper += Vec3(radius*0.5f);
+
+ Vec3 center = 0.5f*(upper + lower);
+
+ float width = (upper - lower).x*0.5f;
+ float depth = (upper - lower).z*0.5f;
+ float edge = 0.0075f*0.5f;
+ float height = 8 * radius;
+
+ AddBox(Vec3(edge, height, depth), center + Vec3(-width, height / 2, 0.0f));
+ AddBox(Vec3(edge, height, depth), center + Vec3(width, height / 2, 0.0f));
+ AddBox(Vec3(width - edge, height, edge), center + Vec3(0.0f, height / 2, (depth - edge)));
+ AddBox(Vec3(width - edge, height, edge), center + Vec3(0.0f, height / 2, -(depth - edge)));
+ AddBox(Vec3(width, edge, depth), Vec3(center.x, lower.y, center.z));
+
+ g_params.gravity[1] = -9.f;
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.0f;
+ g_params.fluid = false;
+ g_params.numIterations = 5;
+ g_params.numPlanes = 1;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = radius;
+ g_params.particleCollisionMargin = radius*0.5f;
+
+ g_params.relaxationFactor = 0.0f;
+ g_numSubsteps = 2;
+
+ g_lightDistance *= 0.85f;
+
+ // draw options
+ g_drawPoints = true;
+ g_warmup = false;
+ }
+}; \ No newline at end of file
diff --git a/demo/scenes/trianglecollision.h b/demo/scenes/trianglecollision.h
new file mode 100644
index 0000000..65a7824
--- /dev/null
+++ b/demo/scenes/trianglecollision.h
@@ -0,0 +1,47 @@
+
+class TriangleCollision : public Scene
+{
+public:
+
+ TriangleCollision(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float radius = 0.05f;
+ CreateParticleGrid(Vec3(0.4f, 1.0f + radius*0.5f, 0.1f), 10, 5, 10, radius, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), 0.0f);
+
+ Mesh* disc = CreateDiscMesh(1.0f, 4);
+
+ // create shallow bowl
+ disc->m_positions[0].y -= 0.5f;
+ disc->CalculateNormals();
+
+
+ NvFlexTriangleMeshId mesh1 = CreateTriangleMesh(disc);
+ AddTriangleMesh(mesh1, Vec3(0.0f, 0.5f, 0.0f), Quat(), Vec3(1.0f, 0.5f, 1.0f));
+ AddTriangleMesh(mesh1, Vec3(1.0f, 0.5f, 1.0f), Quat(), Vec3(1.0f, 0.5f, 1.0f));
+
+ NvFlexTriangleMeshId mesh2 = CreateTriangleMesh(disc);
+ AddTriangleMesh(mesh2, Vec3(-1.0f, 0.5f, 1.0f), Quat(), Vec3(1.0f, 0.5f, 1.0f));
+ AddTriangleMesh(mesh2, Vec3(1.0f, 0.5f, -1.0f), Quat(), Vec3(1.0f, 1.0f, 1.0f));
+
+ NvFlexTriangleMeshId mesh3 = CreateTriangleMesh(disc);
+ AddTriangleMesh(mesh3, Vec3(-1.0f, 0.5f, -1.0f), Quat(), Vec3(1.0f, 0.25f, 1.0f));
+
+
+ delete disc;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.025f;
+ g_params.dissipation = 0.0f;
+ g_params.restitution = 0.0;
+ g_params.numIterations = 4;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+
+ g_numSubsteps = 1;
+
+ // draw options
+ g_drawPoints = true;
+ }
+
+};
diff --git a/demo/scenes/triggervolume.h b/demo/scenes/triggervolume.h
new file mode 100644
index 0000000..0e4c1a4
--- /dev/null
+++ b/demo/scenes/triggervolume.h
@@ -0,0 +1,81 @@
+
+
+
+class TriggerVolume : public Scene
+{
+public:
+
+ TriggerVolume(const char* name) : Scene(name) {}
+
+ void Initialize()
+ {
+ float radius = 0.05f;
+ CreateParticleGrid(Vec3(1.75, 2.0, -0.25), 10, 5, 10, radius, Vec3(0.0f), 1.0f, false, 0.0f, NvFlexMakePhase(0, eNvFlexPhaseSelfCollide), 0.0f);
+
+ g_numExtraParticles = 10000;
+
+ // regular box
+ AddBox(Vec3(0.5), Vec3(0.0, 0.5, 0.0));
+
+ // trigger box
+ AddBox(Vec3(0.5), Vec3(2.0f, 0.5, 0.0));
+ g_buffers->shapeFlags[1] |= eNvFlexShapeFlagTrigger;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.025f;
+ g_params.dissipation = 0.0f;
+ g_params.restitution = 0.0;
+ g_params.numIterations = 4;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+
+ g_numSubsteps = 1;
+
+ // draw options
+ g_drawPoints = true;
+
+ g_emitters[0].mEnabled = true;
+ }
+
+ virtual void Update()
+ {
+ const int maxContactsPerParticle = 6;
+
+ NvFlexVector<Vec4> contactPlanes(g_flexLib,g_buffers->positions.size()*maxContactsPerParticle);
+ NvFlexVector<Vec4> contactVelocities(g_flexLib, g_buffers->positions.size()*maxContactsPerParticle);
+ NvFlexVector<int> contactIndices(g_flexLib, g_buffers->positions.size());
+ NvFlexVector<unsigned int> contactCounts(g_flexLib, g_buffers->positions.size());
+
+ NvFlexGetContacts(g_flex, contactPlanes.buffer, contactVelocities.buffer, contactIndices.buffer, contactCounts.buffer);
+
+ contactPlanes.map();
+ contactVelocities.map();
+ contactIndices.map();
+ contactCounts.map();
+
+ int activeCount = NvFlexGetActiveCount(g_flex);
+
+ for (int i = 0; i < activeCount; ++i)
+ {
+ const int contactIndex = contactIndices[i];
+ const unsigned int count = contactCounts[contactIndex];
+
+ for (unsigned int c = 0; c < count; ++c)
+ {
+ Vec4 velocity = contactVelocities[contactIndex*maxContactsPerParticle + c];
+
+ const int shapeId = int(velocity.w);
+
+ // detect when particle intersects the trigger
+ // volume and teleport it over to the other box
+ if (shapeId == 1)
+ {
+ Vec3 pos = Vec3(Randf(-0.5f, 0.5f), 1.0f, Randf(-0.5f, 0.5f));
+
+ g_buffers->positions[i] = Vec4(pos, 1.0f);
+ g_buffers->velocities[i] = 0.0f;
+ }
+ }
+ }
+ }
+
+}; \ No newline at end of file
diff --git a/demo/scenes/viscosity.h b/demo/scenes/viscosity.h
new file mode 100644
index 0000000..0615855
--- /dev/null
+++ b/demo/scenes/viscosity.h
@@ -0,0 +1,79 @@
+
+class Viscosity : public Scene
+{
+public:
+
+ Viscosity(const char* name, float viscosity = 1.0f, float dissipation = 0.0f) : Scene(name), viscosity(viscosity), dissipation(dissipation) {}
+
+ virtual void Initialize()
+ {
+ float radius = 0.1f;
+ float restDistance = radius*0.5f;
+
+ g_params.radius = radius;
+
+ g_params.fluid = true;
+ g_params.numIterations = 3;
+ g_params.vorticityConfinement = 0.0f;
+ g_params.fluidRestDistance = restDistance;
+ g_params.anisotropyScale = 3.0f / radius;
+ g_params.smoothing = 0.35f;
+ g_params.relaxationFactor = 1.f;
+ g_params.restitution = 0.0f;
+ g_params.collisionDistance = 0.00125f;
+ g_params.shapeCollisionMargin = g_params.collisionDistance*0.25f;
+ g_params.dissipation = dissipation;
+
+ g_params.gravity[1] *= 2.0f;
+
+ g_fluidColor = Vec4(1.0f, 1.0f, 1.0f, 0.0f);
+ g_meshColor = Vec3(0.7f, 0.8f, 0.9f)*0.7f;
+
+ g_params.dynamicFriction = 1.0f;
+ g_params.staticFriction = 0.0f;
+ g_params.viscosity = 20.0f + 20.0f*viscosity;
+ g_params.adhesion = 0.1f*viscosity;
+ g_params.cohesion = 0.05f*viscosity;
+ g_params.surfaceTension = 0.0f;
+
+ const float shapeSize = 2.0f;
+ const Vec3 shapeLower = Vec3(-shapeSize*0.5f, 0.0f, -shapeSize*0.5f);
+ const Vec3 shapeUpper = shapeLower + Vec3(shapeSize);
+ const Vec3 shapeCenter = (shapeLower + shapeUpper)*0.5f;
+
+ NvFlexDistanceFieldId sdf = CreateSDF(GetFilePathByPlatform("../../data/bunny.ply").c_str(), 128);
+ AddSDF(sdf, shapeLower, Quat(), shapeSize);
+
+ float emitterSize = 1.f;
+
+ Emitter e;
+ e.mEnabled = true;
+ e.mWidth = int(emitterSize / restDistance);
+ e.mPos = Vec3(shapeCenter.x - 0.2f, shapeUpper.y + 0.75f, shapeCenter.z);
+ e.mDir = Vec3(0.0f, -1.0f, 0.0f);
+ e.mRight = Vec3(1.0f, 0.0f, 0.0f);
+ e.mSpeed = (restDistance*2.f / g_dt);
+
+ g_sceneUpper.z = 5.0f;
+
+ g_emitters.push_back(e);
+
+ g_numExtraParticles = 64 * 1024;
+
+ g_lightDistance *= 2.5f;
+
+ // draw options
+ g_drawEllipsoids = true;
+
+ g_emit = true;
+ g_pause = false;
+ }
+
+ virtual void DoGui()
+ {
+ imguiSlider("Emitter Pos", &g_emitters.back().mPos.x, -1.0f, 1.0f, 0.001f);
+ }
+
+ float viscosity;
+ float dissipation;
+}; \ No newline at end of file
diff --git a/demo/scenes/waterballoon.h b/demo/scenes/waterballoon.h
new file mode 100644
index 0000000..544bf78
--- /dev/null
+++ b/demo/scenes/waterballoon.h
@@ -0,0 +1,336 @@
+
+class WaterBalloon : public Scene
+{
+public:
+
+ WaterBalloon(const char* name) : Scene(name) {}
+
+ virtual ~WaterBalloon()
+ {
+ for (size_t i = 0; i < mCloths.size(); ++i)
+ NvFlexExtDestroyTearingCloth(mCloths[i].asset);
+ }
+
+ void AddInflatable(const Mesh* mesh, float overPressure, float invMass, int phase)
+ {
+ // create a cloth mesh using the global positions / indices
+ const int numParticles = int(mesh->m_positions.size());
+ const int maxParticles = numParticles * 2;
+
+ Balloon balloon;
+ balloon.particleOffset = g_buffers->positions.size();
+ balloon.triangleOffset = g_buffers->triangles.size();
+ balloon.splitThreshold = 4.0f;
+
+ // add particles to system
+ for (size_t i = 0; i < mesh->GetNumVertices(); ++i)
+ {
+ const Vec3 p = Vec3(mesh->m_positions[i]);
+
+ g_buffers->positions.push_back(Vec4(p.x, p.y, p.z, invMass));
+ g_buffers->restPositions.push_back(Vec4(p.x, p.y, p.z, invMass));
+
+ g_buffers->velocities.push_back(0.0f);
+ g_buffers->phases.push_back(phase);
+ }
+
+ for (size_t i = 0; i < mesh->m_indices.size(); i += 3)
+ {
+ int a = mesh->m_indices[i + 0];
+ int b = mesh->m_indices[i + 1];
+ int c = mesh->m_indices[i + 2];
+
+ Vec3 n = -Normalize(Cross(mesh->m_positions[b] - mesh->m_positions[a], mesh->m_positions[c] - mesh->m_positions[a]));
+ g_buffers->triangleNormals.push_back(n);
+
+ g_buffers->triangles.push_back(a + balloon.particleOffset);
+ g_buffers->triangles.push_back(b + balloon.particleOffset);
+ g_buffers->triangles.push_back(c + balloon.particleOffset);
+ }
+
+ // create tearing asset
+ NvFlexExtAsset* cloth = NvFlexExtCreateTearingClothFromMesh((float*)&g_buffers->positions[balloon.particleOffset], numParticles, maxParticles, (int*)&mesh->m_indices[0], mesh->GetNumFaces(), 1.0f, 1.0f, 0.0f);
+ balloon.asset = cloth;
+
+ mCloths.push_back(balloon);
+ }
+
+ void Initialize()
+ {
+ mCloths.resize(0);
+
+ float minSize = 0.25f;
+ float maxSize = 0.5f;
+ float spacing = 4.0f;
+
+ // convex rocks
+ for (int i = 0; i < 4; i++)
+ for (int j = 0; j < 1; j++)
+ AddRandomConvex(10, Vec3(i*maxSize*spacing, 0.0f, j*maxSize*spacing), minSize, maxSize, Vec3(0.0f, 1.0f, 0.0f), Randf(0.0f, k2Pi));
+
+ float radius = 0.1f;
+ int group = 0;
+
+ g_numExtraParticles = 20000;
+ g_numSubsteps = 3;
+
+ g_params.radius = radius;
+ g_params.dynamicFriction = 0.125f;
+ g_params.dissipation = 0.0f;
+ g_params.numIterations = 5;
+ g_params.particleCollisionMargin = g_params.radius*0.05f;
+ g_params.relaxationFactor = 1.0f;
+ g_params.drag = 0.0f;
+ g_params.anisotropyScale = 25.0f;
+ g_params.smoothing = 1.f;
+ g_params.maxSpeed = 0.5f*g_numSubsteps*radius / g_dt;
+ g_params.gravity[1] *= 1.0f;
+ g_params.collisionDistance = 0.01f;
+ g_params.solidPressure = 0.0f;
+
+ g_params.fluid = true;
+
+ g_params.fluidRestDistance = radius*0.65f;
+ g_params.viscosity = 0.0;
+ g_params.adhesion = 0.0f;
+ g_params.cohesion = 0.02f;
+
+
+ // add inflatables
+ Mesh* mesh = ImportMesh(GetFilePathByPlatform("../../data/sphere_high.ply").c_str());
+
+ for (int y = 0; y < 2; ++y)
+ for (int i = 0; i < 2; ++i)
+ {
+ Vec3 lower = Vec3(2.0f + i*2.0f, 0.4f + y*1.2f, 1.0f);
+
+ mesh->Normalize();
+ mesh->Transform(TranslationMatrix(Point3(lower)));
+
+ AddInflatable(mesh, 1.0f, 0.25f, NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseSelfCollideFilter));
+ }
+
+ g_numSolidParticles = g_buffers->positions.size();
+ g_numExtraParticles = g_buffers->positions.size();
+
+ // fill inflatables with water
+ std::vector<Vec3> positions(10000);
+ int n = PoissonSample3D(0.45f, g_params.radius*0.42f, &positions[0], positions.size(), 10000);
+ //int n = TightPack3D(0.45f, g_params.radius*0.42f, &positions[0], positions.size());
+
+ mNumFluidParticles = 0;
+
+ for (size_t i = 0; i < mCloths.size(); ++i)
+ {
+ const int vertStart = i*mesh->GetNumVertices();
+ const int vertEnd = vertStart + mesh->GetNumVertices();
+
+ const int phase = NvFlexMakePhase(group++, eNvFlexPhaseSelfCollide | eNvFlexPhaseFluid);
+
+ Vec3 center;
+ for (int v = vertStart; v < vertEnd; ++v)
+ center += Vec3(g_buffers->positions[v]);
+
+ center /= float(vertEnd - vertStart);
+
+ printf("%d, %d - %f %f %f\n", vertStart, vertEnd, center.x, center.y, center.z);
+
+ for (int i = 0; i < n; ++i)
+ {
+ g_buffers->positions.push_back(Vec4(center + positions[i], 1.0f));
+ g_buffers->restPositions.push_back(Vec4());
+ g_buffers->velocities.push_back(0.0f);
+ g_buffers->phases.push_back(phase);
+ }
+
+ mNumFluidParticles += n;
+ }
+
+ delete mesh;
+
+ g_drawPoints = false;
+ g_drawEllipsoids = true;
+ g_drawSprings = 0;
+ g_drawCloth = false;
+ g_warmup = true;
+
+ }
+
+ void RebuildConstraints()
+ {
+ // update constraint data
+ g_buffers->triangles.resize(0);
+ g_buffers->springIndices.resize(0);
+ g_buffers->springStiffness.resize(0);
+ g_buffers->springLengths.resize(0);
+
+ for (int c = 0; c < int(mCloths.size()); ++c)
+ {
+ Balloon& balloon = mCloths[c];
+
+ for (int i = 0; i < balloon.asset->numTriangles; ++i)
+ {
+ g_buffers->triangles.push_back(balloon.asset->triangleIndices[i * 3 + 0] + balloon.particleOffset);
+ g_buffers->triangles.push_back(balloon.asset->triangleIndices[i * 3 + 1] + balloon.particleOffset);
+ g_buffers->triangles.push_back(balloon.asset->triangleIndices[i * 3 + 2] + balloon.particleOffset);
+ }
+
+ for (int i = 0; i < balloon.asset->numSprings * 2; ++i)
+ g_buffers->springIndices.push_back(balloon.asset->springIndices[i] + balloon.particleOffset);
+
+
+ for (int i = 0; i < balloon.asset->numSprings; ++i)
+ {
+ g_buffers->springStiffness.push_back(balloon.asset->springCoefficients[i]);
+ g_buffers->springLengths.push_back(balloon.asset->springRestLengths[i]);
+ }
+ }
+ }
+
+ virtual void Sync()
+ {
+ // send new particle data to the GPU
+ NvFlexSetRestParticles(g_flex, g_buffers->restPositions.buffer, g_buffers->restPositions.size());
+
+ // update solver
+ NvFlexSetSprings(g_flex, g_buffers->springIndices.buffer, g_buffers->springLengths.buffer, g_buffers->springStiffness.buffer, g_buffers->springLengths.size());
+ NvFlexSetDynamicTriangles(g_flex, g_buffers->triangles.buffer, g_buffers->triangleNormals.buffer, g_buffers->triangles.size() / 3);
+ }
+
+ virtual void Update()
+ {
+ // temporarily restore the mouse particle's mass so that we can tear it
+ if (g_mouseParticle != -1)
+ g_buffers->positions[g_mouseParticle].w = g_mouseMass;
+
+ // force larger radius for solid interactions to prevent interpenetration
+ g_params.solidRestDistance = g_params.radius;
+
+ // build new particle arrays
+ std::vector<Vec4> newParticles;
+ std::vector<Vec4> newParticlesRest;
+ std::vector<Vec3> newVelocities;
+ std::vector<int> newPhases;
+ std::vector<Vec4> newNormals;
+
+ for (int c = 0; c < int(mCloths.size()); ++c)
+ {
+ Balloon& balloon = mCloths[c];
+
+ const int destOffset = newParticles.size();
+
+ // append existing particles
+ for (int i = 0; i < balloon.asset->numParticles; ++i)
+ {
+ newParticles.push_back(g_buffers->positions[balloon.particleOffset + i]);
+ newParticlesRest.push_back(g_buffers->restPositions[balloon.particleOffset + i]);
+ newVelocities.push_back(g_buffers->velocities[balloon.particleOffset + i]);
+ newPhases.push_back(g_buffers->phases[balloon.particleOffset + i]);
+ newNormals.push_back(g_buffers->normals[balloon.particleOffset + i]);
+ }
+
+ // perform splitting
+ const int maxCopies = 2048;
+ const int maxEdits = 2048;
+
+ NvFlexExtTearingParticleClone particleCopies[maxCopies];
+ int numParticleCopies;
+
+ NvFlexExtTearingMeshEdit triangleEdits[maxEdits];
+ int numTriangleEdits;
+
+ // update asset's copy of the particles
+ memcpy(balloon.asset->particles, &g_buffers->positions[balloon.particleOffset], sizeof(Vec4)*balloon.asset->numParticles);
+
+ // tear
+ NvFlexExtTearClothMesh(balloon.asset, balloon.splitThreshold, 1, particleCopies, &numParticleCopies, maxCopies, triangleEdits, &numTriangleEdits, maxEdits);
+
+ // resize particle data arrays
+ newParticles.resize(newParticles.size() + numParticleCopies);
+ newParticlesRest.resize(newParticlesRest.size() + numParticleCopies);
+ newVelocities.resize(newVelocities.size() + numParticleCopies);
+ newPhases.resize(newPhases.size() + numParticleCopies);
+ newNormals.resize(newNormals.size() + numParticleCopies);
+
+ // copy particles
+ for (int i = 0; i < numParticleCopies; ++i)
+ {
+ const int srcIndex = balloon.particleOffset + particleCopies[i].srcIndex;
+ const int destIndex = destOffset + particleCopies[i].destIndex;
+
+ newParticles[destIndex] = g_buffers->positions[srcIndex];
+ newParticlesRest[destIndex] = g_buffers->restPositions[srcIndex];
+ newVelocities[destIndex] = g_buffers->velocities[srcIndex];
+ newPhases[destIndex] = g_buffers->phases[srcIndex];
+ newNormals[destIndex] = g_buffers->normals[srcIndex];
+ }
+
+ if (numParticleCopies)
+ {
+ // reduce split threshold for this balloon
+ balloon.splitThreshold = 1.75f;
+ }
+
+ balloon.particleOffset = destOffset;
+ balloon.asset->numParticles += numParticleCopies;
+ }
+
+ // append fluid particles
+ const int fluidStart = g_numSolidParticles;
+ const int fluidEnd = fluidStart + mNumFluidParticles;
+
+ g_numSolidParticles = newParticles.size();
+
+ for (int i = fluidStart; i < fluidEnd; ++i)
+ {
+ newParticles.push_back(g_buffers->positions[i]);
+ newParticlesRest.push_back(Vec4());
+ newVelocities.push_back(g_buffers->velocities[i]);
+ newPhases.push_back(g_buffers->phases[i]);
+ newNormals.push_back(g_buffers->normals[i]);
+ }
+
+ g_buffers->positions.assign(&newParticles[0], newParticles.size());
+ g_buffers->restPositions.assign(&newParticlesRest[0], newParticlesRest.size());
+ g_buffers->velocities.assign(&newVelocities[0], newVelocities.size());
+ g_buffers->phases.assign(&newPhases[0], newPhases.size());
+ g_buffers->normals.assign(&newNormals[0], newNormals.size());
+
+ // build active indices list
+ g_buffers->activeIndices.resize(g_buffers->positions.size());
+ for (int i = 0; i < g_buffers->positions.size(); ++i)
+ g_buffers->activeIndices[i] = i;
+
+ // update constraint buffers
+ RebuildConstraints();
+
+ // restore mouse mass
+ if (g_mouseParticle != -1)
+ g_buffers->positions[g_mouseParticle].w = 0.0f;
+ }
+
+ virtual void Draw(int pass)
+ {
+ if (!g_drawMesh)
+ return;
+
+ for (size_t i = 0; i < mCloths.size(); ++i)
+ {
+ DrawCloth(&g_buffers->positions[0], &g_buffers->normals[0], NULL, &g_buffers->triangles[mCloths[i].triangleOffset], mCloths[i].asset->numTriangles, g_buffers->positions.size(), (i + 2) % 6);//, g_params.radius*0.25f);
+ }
+ }
+
+ struct Balloon
+ {
+ NvFlexExtAsset* asset;
+
+ int particleOffset;
+ int triangleOffset;
+
+ float splitThreshold;
+ };
+
+ int mNumFluidParticles;
+
+ std::vector<Balloon> mCloths;
+};