
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;
};