// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.

#include "extensions/PxSamplingExt.h"
#include "GuSDF.h"
#include "foundation/PxQuat.h"
#include "foundation/PxBounds3.h"
#include "foundation/PxHashSet.h"
#include "GuDistancePointTriangle.h"
#include "extensions/PxRemeshingExt.h"
#include "geometry/PxGeometryQuery.h"
#include "PxQueryReport.h"
#include "foundation/PxHashMap.h"
#include "CmRandom.h"

#include "GuAABBTreeNode.h"
#include "GuAABBTree.h"
#include "GuAABBTreeBounds.h"
#include "GuWindingNumber.h"
#include "foundation/PxMathUtils.h"
#include "foundation/PxSort.h"

using namespace physx;
using namespace Gu;
using namespace Cm;	

static const PxI32 neighborEdges[3][2] = { { 0, 1 }, { 2, 0 }, { 1, 2 } };

namespace
{
	struct Int3
	{
		PxI32 x;
		PxI32 y;
		PxI32 z;

		Int3(PxI32 x_, PxI32 y_, PxI32 z_) : x(x_), y(y_), z(z_) {}

		Int3() : x(0), y(0), z(0) {}
	};

	struct ActiveSample
	{
		PxI32 mIndex;
		PxArray<PxI32> mNearbyTriangles;
		PxArray<PxReal> mCumulativeTriangleAreas;

		ActiveSample() : mIndex(-1) {}

		ActiveSample(PxI32 index, const PxArray<PxI32>& nearbyTriangles, const PxArray<PxReal>& cumulativeTriangleAreas)
		{
			mIndex = index;
			mNearbyTriangles = nearbyTriangles;
			mCumulativeTriangleAreas = cumulativeTriangleAreas;
		}
	};

	struct PointWithNormal
	{
		PxVec3 mPoint;
		PxVec3 mNormal;

		PointWithNormal() {}

		PointWithNormal(const PxVec3& point, const PxVec3& normal) : mPoint(point), mNormal(normal)
		{
		}
	};

	struct IndexWithNormal
	{
		PxI32 mIndex;
		PxVec3 mNormal;

		IndexWithNormal(PxI32 index, const PxVec3& normal) : mIndex(index), mNormal(normal)
		{
		}
	};
}

// PT: TODO: refactor with other SDK implementation
static void getBoundsFromPoints(const PxVec3* points, const PxU32 numPoints, PxVec3& outMinExtents, PxVec3& outMaxExtents)
{
	PxVec3 minExtents(FLT_MAX);
	PxVec3 maxExtents(-FLT_MAX);

	// calculate face bounds
	for (PxU32 i = 0; i < numPoints; ++i)
	{
		const PxVec3& a = points[i];

		minExtents = a.minimum(minExtents);
		maxExtents = a.maximum(maxExtents);
	}

	outMinExtents = minExtents;
	outMaxExtents = maxExtents;
}

static PX_FORCE_INLINE PxReal triArea(const PxVec3& a, const PxVec3& b, const PxVec3& c)
{
	return 0.5f * (b - a).cross(c - a).magnitude();
}

static PX_FORCE_INLINE PxReal triArea(const PxU32* tri, const PxVec3* points)
{
	return triArea(points[tri[0]], points[tri[1]], points[tri[2]]);
}

static PX_FORCE_INLINE PxU64 edgeKey(PxI32 a, PxI32 b)
{
	if (a < b)
		return ((PxU64(a)) << 32) | (PxU64(b));
	else
		return ((PxU64(b)) << 32) | (PxU64(a));
}

static void buildTriangleAdjacency(const PxU32* tris, PxU32 numTriangles, PxArray<PxI32>& result)
{
	PxU32 l = 4 * numTriangles; //4 elements per triangle - waste one entry per triangle to get a power of 2 which allows for bit shift usage instead of modulo	
	result.clear();
	result.resize(l, -1);

	for (PxU32 i = 3; i < l; i += 4)
		result[i] = -2;

	PxHashMap<PxU64, PxU32> edges;
	for (PxU32 i = 0; i < numTriangles; ++i)
	{
		const PxU32* tri = &tris[3 * i];

		for (PxU32 j = 0; j < 3; ++j)
		{
			const PxU64 edge = edgeKey(tri[neighborEdges[j][0]], tri[neighborEdges[j][1]]);
			const PxPair<const PxU64, PxU32>* it = edges.find(edge);
			if (it)
			{
				result[4u * i + j] = it->second;
				result[it->second] = 4u * i + j;
			}
			else
			{
				PxU32 v = 4u * i + j;
				edges.insert(edge, v);
			}
		}
	}
}

static void collectTrianglesInSphere(const PxVec3& center, PxReal radius, PxI32 startTri, const PxU32* triangles, const PxVec3* points,
	const PxArray<PxI32>& adj, PxHashSet<PxI32>& result)
{
	PxArray<PxI32> stack;
	stack.pushBack(startTri);

	result.clear();
	result.insert(startTri);

	while (stack.size() > 0)
	{
		PxI32 tri = stack.popBack() * 4;
		for (PxI32 i = 0; i < 3; ++i)
		{
			PxI32 n = adj[tri + i] >> 2;
			if (n >= 0 && !result.contains(n))
			{
				const PxU32* t = &triangles[3 * n];
				if (Gu::distancePointTriangleSquared(center, points[t[0]], points[t[1]] - points[t[0]], points[t[2]] - points[t[0]]) < radius * radius)
				{
					result.insert(n);
					stack.pushBack(n);
				}
			}
		}
	}
}

static void createActiveSample(const PxArray<PxReal>& triangleAreaBuffer, PxI32 sampleIndex, const PxVec3& sample, PxReal radius, PxI32 startTri,
	const PxU32* triangles, const PxVec3* points, const PxArray<PxI32>& adj, ActiveSample& result)
{
	PxHashSet<PxI32> nearbyTriangles;
	collectTrianglesInSphere(sample, radius, startTri, triangles, points, adj, nearbyTriangles);

	result.mNearbyTriangles.clear();
	result.mNearbyTriangles.reserve(nearbyTriangles.size());
	//for (PxI32 t : nearbyTriangles)
	for (PxHashSet<PxI32>::Iterator iter = nearbyTriangles.getIterator(); !iter.done(); ++iter)
		result.mNearbyTriangles.pushBack(*iter);

	result.mCumulativeTriangleAreas.clear();
	result.mCumulativeTriangleAreas.resize(nearbyTriangles.size());
	result.mCumulativeTriangleAreas[0] = triangleAreaBuffer[result.mNearbyTriangles[0]];
	for (PxU32 i = 1; i < nearbyTriangles.size(); ++i)
		result.mCumulativeTriangleAreas[i] = result.mCumulativeTriangleAreas[i - 1] + triangleAreaBuffer[result.mNearbyTriangles[i]];

	result.mIndex = sampleIndex;
}

//Returns the index of the element with value <= v
static PxU32 binarySearch(const PxArray<PxReal>& sorted, PxReal v)
{
	PxU32 low = 0;
	PxU32 up = PxU32(sorted.size());

	while (up - low > 1)
	{
		PxU32 middle = (up + low) >> 1;
		PxReal m = sorted[middle];
		if (v <= m)
			up = middle;
		else
			low = middle;
	}
	return low;
}

static PxVec3 randomPointOnTriangle(BasicRandom& rnd, const PxU32* tri, const PxVec3* points, PxVec3* barycentricCoordinates = NULL)
{
	while (true)
	{
		PxReal a = rnd.rand(0.0f, 1.0f);
		PxReal b = rnd.rand(0.0f, 1.0f);

		PxReal sum = a + b;
		if (sum > 1)
			continue;

		PxReal c = 1 - a - b;
		if (barycentricCoordinates)
			(*barycentricCoordinates) = PxVec3(a, b, c);
		return points[tri[0]] * a + points[tri[1]] * b + points[tri[2]] * c;
	}
}

static bool samplePointInBallOnSurface(BasicRandom& rnd, const PxArray<PxReal>& cumulativeAreas, const PxVec3* points, const PxU32* triangles, const PxArray<PxI32>& nearbyTriangles,
	const PxVec3& point, PxReal radius, PxVec3& sample, PxI32& triId, PxI32 numAttempts = 30, PxVec3* barycentricCoordinates = NULL)
{
	triId = -1;

	//Use variable upper bound as described in http://extremelearning.com.au/an-improved-version-of-bridsons-algorithm-n-for-poisson-disc-sampling/
	PxReal step = radius / numAttempts;
	PxReal rUpper = radius + step;
	PxVec3 fallback;
	PxReal fallbackDist = FLT_MAX;
	PxI32 fallbackId = -1;
	PxVec3 fallbackBary;
	for (PxI32 i = 0; i < numAttempts; ++i)
	{
		PxReal totalArea = cumulativeAreas[cumulativeAreas.size() - 1];
		PxReal r = rnd.rand(0.0f, 1.0f) * totalArea;
		PxI32 id;
		id = binarySearch(cumulativeAreas, r);

		triId = nearbyTriangles[id];
		sample = randomPointOnTriangle(rnd, &triangles[3 * triId], points, barycentricCoordinates);

		const PxReal dist2 = (sample - point).magnitudeSquared();
		if (dist2 > radius * radius && dist2 < rUpper * rUpper)
			return true;
		if (dist2 > radius * radius && dist2 < 4 * radius * radius && dist2 < fallbackDist)
		{
			fallbackDist = dist2;
			fallbackId = triId;
			fallback = sample;
			if (barycentricCoordinates)
				fallbackBary = *barycentricCoordinates;
		}
		rUpper += step;
	}
	if (fallbackId >= 0)
	{
		sample = fallback;
		triId = fallbackId;
		if (barycentricCoordinates)
			*barycentricCoordinates = fallbackBary;
		return true;
	}

	return false;
}

namespace
{
	class PoissonSamplerShared
	{
	private:
		struct SparseGridNode
		{
			PxI32 mPointIndex;
			PxI32 mExcessStartIndex;
			PxI32 mExcessEndIndex;

			SparseGridNode(PxI32 pointIndex_) : mPointIndex(pointIndex_), mExcessStartIndex(0), mExcessEndIndex(-1)
			{
			}

			SparseGridNode() : mPointIndex(-1), mExcessStartIndex(0), mExcessEndIndex(-1)
			{ }
		};

		//Returns true if successful. False if too many cells are required (overflow)
		bool rebuildSparseGrid();

	public:
		PoissonSamplerShared() : maxNumSamples(0), currentSamplingRadius(0.0f) {}

		//Returns true if successful. False if too many cells are required (overflow)
		bool setSamplingRadius(PxReal r);

		void addSamples(const PxArray<PxVec3>& samples);

		PxU32 removeSamples(const PxArray<PxVec3>& samples);

		PxI32 findSample(const PxVec3& p);

		PxReal minDistanceToOtherSamplesSquared(const PxVec3& p) const;

		const PxArray<PxVec3>& getSamples() const { return result; }

		bool addPointToSparseGrid(const PxVec3& p, PxI32 pointIndex);

	protected:
		bool postAddPointToSparseGrid(const PxVec3& p, PxI32 pointIndex);
		bool preAddPointToSparseGrid(const PxVec3& p, PxI32 pointIndex);		

	public:
		//Input
		PxI32 numSampleAttemptsAroundPoint;
		PxVec3 size;
		PxVec3 min;
		PxU32 maxNumSamples;

		//Intermediate data
		PxReal currentSamplingRadius;
		Int3 resolution;
		PxReal cellSize;
		PxArray<PxU32> occupiedCellBits;
		PxHashMap<PxI32, SparseGridNode> sparseGrid3D;
		PxArray<PxI32> excessList;
		Cm::BasicRandom rnd;

		bool gridResolutionValid = false;

		//Output
		PxArray<PxVec3> result;

		PX_NOCOPY(PoissonSamplerShared)
	};

	struct PointInVolumeTester
	{
		virtual bool pointInVolume(const PxVec3& p) const = 0;

		virtual ~PointInVolumeTester() {}
	};
}

static PX_FORCE_INLINE bool pointInSphere(const PxVec3& p, const PxVec3& sphereCenter, PxReal sphereRadius)
{
	return (p - sphereCenter).magnitudeSquared() < sphereRadius * sphereRadius;
}

namespace
{
	struct AlwaysInsideTester : public PointInVolumeTester
	{
		AlwaysInsideTester() {}

		virtual bool pointInVolume(const PxVec3&) const
		{
			return true;
		}

		virtual ~AlwaysInsideTester() {}
	};

	struct PointInSphereTester : public PointInVolumeTester
	{
		PxVec3 mCenter;
		PxReal mRadius;

		PointInSphereTester(const PxVec3& center, const PxReal radius) : mCenter(center), mRadius(radius) {}

		virtual bool pointInVolume(const PxVec3& p) const
		{
			return pointInSphere(p, mCenter, mRadius);
		}

		virtual ~PointInSphereTester() {}
	};

	struct PointInOBBTester : public PointInVolumeTester
	{
		PxVec3 mBoxCenter;
		PxVec3 mBoxAxisAlignedExtents;
		PxQuat mBoxOrientation;

		PointInOBBTester(const PxVec3& boxCenter, const PxVec3& boxAxisAlignedExtents, const PxQuat boxOrientation) 
			: mBoxCenter(boxCenter), mBoxAxisAlignedExtents(boxAxisAlignedExtents), mBoxOrientation(boxOrientation) {}

		virtual bool pointInVolume(const PxVec3& p) const
		{
			PxVec3 localPoint = mBoxOrientation.rotateInv(p - mBoxCenter);
			return localPoint.x >= -mBoxAxisAlignedExtents.x && localPoint.x <= mBoxAxisAlignedExtents.x &&
				localPoint.y >= -mBoxAxisAlignedExtents.y && localPoint.y <= mBoxAxisAlignedExtents.y &&
				localPoint.z >= -mBoxAxisAlignedExtents.z && localPoint.z <= mBoxAxisAlignedExtents.z;
		}

		virtual ~PointInOBBTester() {}
	};

	class TriangleMeshPoissonSampler : public PxTriangleMeshPoissonSampler
	{
	public:
		TriangleMeshPoissonSampler(const PxU32* tris, PxU32 numTris, const PxVec3* pts_, PxU32 numPts, PxReal r, PxI32 numSampleAttemptsAroundPoint_ = 30, PxU32 maxNumSamples_ = 0);

		virtual void addSamplesInVolume(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, bool createVolumeSamples);

		virtual void addSamplesInSphere(const PxVec3& sphereCenter, PxReal sphereRadius, bool createVolumeSamples);

		virtual void addSamplesInBox(const PxBounds3& axisAlignedBox, const PxQuat& boxOrientation, bool createVolumeSamples);

		virtual const PxArray<PxI32>& getSampleTriangleIds() const { return triangleIds; }

		virtual const PxArray<PxVec3>& getSampleBarycentrics() const { return barycentricCoordinates; }

		virtual bool setSamplingRadius(PxReal samplingRadius) { return poissonSamplerShared.setSamplingRadius(samplingRadius); }

		virtual void addSamples(const PxArray<PxVec3>& samples) { poissonSamplerShared.addSamples(samples); }

		void createVolumeSamples(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, PxReal randomScale, PxReal r, bool addToSparseGrid = true);

		virtual PxU32 removeSamples(const PxArray<PxVec3>& samples) { return poissonSamplerShared.removeSamples(samples); }

		virtual const PxArray<PxVec3>& getSamples() const { return poissonSamplerShared.result; }

		virtual bool isPointInTriangleMesh(const PxVec3& p);

		virtual ~TriangleMeshPoissonSampler() { }

	public:
		bool pointInMesh(const PxVec3& p);

		PoissonSamplerShared poissonSamplerShared;

		//Input
		const PxVec3* originalPoints;
		const PxU32* originalTriangles;
		const PxU32 numOriginalTriangles;

		PxVec3 max;

		PxArray<PxVec3> points;
		PxArray<PxU32> triangles;
		PxArray<PxU32> triangleMap;
		PxArray<PxReal> triangleAreaBuffer;
		PxArray<PxI32> adj;

		PxArray<Gu::BVHNode> tree;
		PxHashMap<PxU32, Gu::ClusterApproximation> clusters;

		//Intermediate data
		PxArray<ActiveSample> activeSamples;

		//Output
		PxArray<PxI32> triangleIds;
		PxArray<PxVec3> barycentricCoordinates;

		PX_NOCOPY(TriangleMeshPoissonSampler)
	};

	class ShapePoissonSampler : public PxPoissonSampler
	{
	public:
		ShapePoissonSampler(const PxGeometry& geometry_, const PxTransform& transform_, const PxBounds3& worldBounds_, PxReal r, PxI32 numSampleAttemptsAroundPoint_ = 30, PxU32 maxNumSamples_ = 0);
		
		virtual void addSamplesInVolume(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, bool createVolumeSamples);

		virtual void addSamplesInSphere(const PxVec3& sphereCenter, PxReal sphereRadius, bool createVolumeSamples);
		
		virtual void addSamplesInBox(const PxBounds3& axisAlignedBox, const PxQuat& boxOrientation, bool createVolumeSamples);

		virtual bool setSamplingRadius(PxReal samplingRadius) { return poissonSamplerShared.setSamplingRadius(samplingRadius); }

		virtual void addSamples(const PxArray<PxVec3>& samples) { poissonSamplerShared.addSamples(samples); }

		void createVolumeSamples(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, PxReal randomScale, PxReal r, bool addToSparseGrid = true);

		virtual PxU32 removeSamples(const PxArray<PxVec3>& samples) { return poissonSamplerShared.removeSamples(samples); }

		virtual const PxArray<PxVec3>& getSamples() const { return poissonSamplerShared.result; }

		virtual ~ShapePoissonSampler() { }

	public:
		PoissonSamplerShared poissonSamplerShared;

		//Input
		const PxGeometry& shape;
		const PxTransform actorGlobalPose;

		//Intermediate data
		PxArray<IndexWithNormal> activeSamples;
	};
}

bool TriangleMeshPoissonSampler::pointInMesh(const PxVec3& p)
{
	return Gu::computeWindingNumber(tree.begin(), p, clusters, originalTriangles, originalPoints) > 0.5f;
}

PxPoissonSampler* physx::PxCreateShapeSampler(const PxGeometry& geometry, const PxTransform& transform, const PxBounds3& worldBounds, PxReal r, PxI32 numSampleAttemptsAroundPoint)
{
	return PX_NEW(ShapePoissonSampler)(geometry, transform, worldBounds, r, numSampleAttemptsAroundPoint);
}

PxTriangleMeshPoissonSampler* physx::PxCreateTriangleMeshSampler(const PxU32* tris, PxU32 numTris, const PxVec3* pts, PxU32 numPts, PxReal r, PxI32 numSampleAttemptsAroundPoint)
{
	return PX_NEW(TriangleMeshPoissonSampler)(tris, numTris, pts, numPts, r, numSampleAttemptsAroundPoint);
}

static PxVec3 computeBarycentricCoordinates(PxVec3 p, const PxVec3& a, PxVec3 b, PxVec3 c)
{
	PxVec4 bary;
	PxComputeBarycentric(a, b, c, p, bary);
	return PxVec3(bary.x, bary.y, bary.z);
}
	
ShapePoissonSampler::ShapePoissonSampler(const PxGeometry& shape_, const PxTransform& actorGlobalPose_, const PxBounds3& worldBounds_, 
	PxReal r, PxI32 numSampleAttemptsAroundPoint_, PxU32 maxNumSamples_) : shape(shape_), actorGlobalPose(actorGlobalPose_)
{
	poissonSamplerShared.size = worldBounds_.maximum - worldBounds_.minimum;
	poissonSamplerShared.min = worldBounds_.minimum;
	poissonSamplerShared.numSampleAttemptsAroundPoint = numSampleAttemptsAroundPoint_;
	poissonSamplerShared.maxNumSamples = maxNumSamples_;

	setSamplingRadius(r);
}

TriangleMeshPoissonSampler::TriangleMeshPoissonSampler(const PxU32* tris, PxU32 numTris, const PxVec3* pts_, PxU32 numPts, PxReal r, PxI32 numSampleAttemptsAroundPoint_, PxU32 maxNumSamples_)
	: originalPoints(pts_), originalTriangles(tris), numOriginalTriangles(numTris)
{
	poissonSamplerShared.currentSamplingRadius = 0.0f;
	poissonSamplerShared.numSampleAttemptsAroundPoint = numSampleAttemptsAroundPoint_;
	poissonSamplerShared.maxNumSamples = maxNumSamples_;
		
	getBoundsFromPoints(originalPoints, numPts, poissonSamplerShared.min, max);
	poissonSamplerShared.size = max - poissonSamplerShared.min;

	points.assign(originalPoints, originalPoints + numPts);
	triangles.assign(tris, tris + 3 * numTris);			
	PxRemeshingExt::limitMaxEdgeLength(triangles, points, 2.0f * r, 100, &triangleMap, PxMax(10000u, 4 * numTris));

	PxU32 numTriangles = triangles.size() / 3;
	triangleAreaBuffer.resize(numTriangles);
	for (PxU32 i = 0; i < numTriangles; ++i)
		triangleAreaBuffer[i] = triArea(&triangles[3 * i], points.begin());

	buildTriangleAdjacency(triangles.begin(), numTriangles, adj);

	setSamplingRadius(r);
}

void PoissonSamplerShared::addSamples(const PxArray<PxVec3>& samples)
{
	if (samples.size() > 0)
	{
		for (PxU32 i = 0; i < samples.size(); ++i)
		{
			result.pushBack(samples[i]);				
		}
		rebuildSparseGrid();
	}
}

PxI32 PoissonSamplerShared::findSample(const PxVec3& p)
{
	PxI32 x = PxI32((p.x - min.x) / cellSize);
	PxI32 y = PxI32((p.y - min.y) / cellSize);
	PxI32 z = PxI32((p.z - min.z) / cellSize);
	if (x >= resolution.x) x = resolution.x - 1;
	if (y >= resolution.y) y = resolution.y - 1;
	if (z >= resolution.z) z = resolution.z - 1;

	PxReal minDist = FLT_MAX;
	PxI32 index = -1;
	for (PxI32 oX = -1; oX <= 1; ++oX)
	{
		for (PxI32 oY = -1; oY <= 1; ++oY)
		{
			for (PxI32 oZ = -1; oZ <= 1; ++oZ)
			{
				const PxI32 xx = x + oX;
				const PxI32 yy = y + oY;
				const PxI32 zz = z + oZ;

				if (xx >= 0 && xx < resolution.x && yy >= 0 && yy < resolution.y && zz >= 0 && zz < resolution.z)
				{
					PxI32 cellIndex = xx + resolution.x * yy + (resolution.x * resolution.y) * zz;
					if ((occupiedCellBits[cellIndex >> 5] & (1u << (cellIndex & 31))) != 0)
					{
						const PxPair<const PxI32, SparseGridNode>* it = sparseGrid3D.find(cellIndex);
						if (it)
						{
							const PxReal dist2 = (result[it->second.mPointIndex] - p).magnitudeSquared();
							if (dist2 < minDist) 
							{
								minDist = dist2;
								index = it->second.mPointIndex;
							}
							if (it->second.mExcessStartIndex >= 0)
							{
								for (PxI32 i = it->second.mExcessStartIndex; i < it->second.mExcessEndIndex; ++i)
								{
									const PxReal dist2_ = (result[excessList[i]] - p).magnitudeSquared();
									if (dist2_ < minDist) 
									{
										minDist = dist2_;
										index = it->second.mPointIndex;
									}
								}
							}
							if (minDist == 0.0f)
							{
								return index;
							}
						}
					}
				}
			}
		}
	}
	return -1;
}

PxU32 PoissonSamplerShared::removeSamples(const PxArray<PxVec3>& samples)
{
	if (samples.size() > 0)
	{
		PxArray<PxI32> samplesToRemove;
		samplesToRemove.reserve(samples.size());
		for (PxU32 i = 0; i < samples.size(); ++i)
		{				
			PxI32 index = findSample(samples[i]);
			PX_ASSERT(samples[i] == result[index]);
			if (index >= 0) 				
				samplesToRemove.pushBack(index);				
		}

		PxSort(samplesToRemove.begin(), samplesToRemove.size());

		PxI32 counter = 0;
		for (PxI32 i = PxI32(samplesToRemove.size()) - 1; i >= 0; --i) 
		{
			result[samplesToRemove[i]] = result[result.size() - 1 - counter];
			++counter;
		}
		result.removeRange(result.size() - counter, counter);
			
		rebuildSparseGrid();
		return samplesToRemove.size();
	}
	return 0;
}

bool PoissonSamplerShared::setSamplingRadius(PxReal r)
{
	if (r != currentSamplingRadius)
	{
		currentSamplingRadius = r;
		return rebuildSparseGrid();
	}
	return gridResolutionValid;
}

bool PoissonSamplerShared::rebuildSparseGrid()
{
	const PxReal dimension = 3.0f;
	cellSize = (currentSamplingRadius / PxSqrt(dimension)) * 0.9999f;

	const PxF64 cellsX = PxF64(size.x) / PxF64(cellSize);
	const PxF64 cellsY = PxF64(size.y) / PxF64(cellSize);
	const PxF64 cellsZ = PxF64(size.z) / PxF64(cellSize);

	resolution = Int3(PxMax(1, PxI32(ceil(cellsX))), PxMax(1, PxI32(ceil(cellsY))), PxMax(1, PxI32(ceil(cellsZ))));
		
	const PxF64 numCellsDbl = PxF64(resolution.x) * PxF64(resolution.y) * PxI64(resolution.z);
	if (numCellsDbl >= (1u << 31) ||
		cellsX >= (1u << 31) || 
		cellsY >= (1u << 31) || 
		cellsZ >= (1u << 31))
	{
		gridResolutionValid = false;
		PxGetFoundation().error(physx::PxErrorCode::eINVALID_PARAMETER, PX_FL, "Internal grid resolution of sampler too high. Either a smaller mesh or a bigger radius must be used.");
		return false;
	}

	gridResolutionValid = true;

	PxU32 numCells = PxU32(resolution.x * resolution.y * resolution.z);

	occupiedCellBits.clear();
	occupiedCellBits.resize((numCells + 32 - 1) / 32, 0);
	sparseGrid3D.clear();

	for (PxU32 i = 0; i < result.size(); ++i)
		preAddPointToSparseGrid(result[i], i);

	PxI32 cumulativeSum = 0;
	for (PxHashMap<PxI32, SparseGridNode>::Iterator iter = sparseGrid3D.getIterator(); !iter.done(); ++iter)
	{
		if (iter->second.mExcessStartIndex > 0)
		{
			PxI32 start = cumulativeSum;
			cumulativeSum += iter->second.mExcessStartIndex;
			iter->second.mExcessStartIndex = start;
			iter->second.mExcessEndIndex = start - 1;
		}
		else
		{
			iter->second.mExcessStartIndex = -1;
		}
	}
	excessList.resize(cumulativeSum);
	for (PxU32 i = 0; i < result.size(); ++i)
		postAddPointToSparseGrid(result[i], i);

	return true;
}

bool PoissonSamplerShared::postAddPointToSparseGrid(const PxVec3& p, PxI32 pointIndex)
{
	PxI32 x = PxI32((p.x - min.x) / cellSize);
	PxI32 y = PxI32((p.y - min.y) / cellSize);
	PxI32 z = PxI32((p.z - min.z) / cellSize);
	if (x >= resolution.x) x = resolution.x - 1;
	if (y >= resolution.y) y = resolution.y - 1;
	if (z >= resolution.z) z = resolution.z - 1;

	PxI32 cellIndex = x + resolution.x * y + (resolution.x * resolution.y) * z;
			
	SparseGridNode& n = sparseGrid3D[cellIndex];		
	if (n.mExcessStartIndex < 0) 
	{
		PX_ASSERT(n.mPointIndex == pointIndex);
		return true;
	}

	if (n.mExcessEndIndex < n.mExcessStartIndex)
	{
		PX_ASSERT(n.mPointIndex == pointIndex);
		n.mExcessEndIndex++;
		return true;
	}
	else
	{
		excessList[n.mExcessEndIndex] = pointIndex;
		n.mExcessEndIndex++;
		return true;
	}
}
		
bool PoissonSamplerShared::preAddPointToSparseGrid(const PxVec3& p, PxI32 pointIndex)
{
	PxI32 x = PxI32((p.x - min.x) / cellSize);
	PxI32 y = PxI32((p.y - min.y) / cellSize);
	PxI32 z = PxI32((p.z - min.z) / cellSize);
	if (x >= resolution.x) x = resolution.x - 1;
	if (y >= resolution.y) y = resolution.y - 1;
	if (z >= resolution.z) z = resolution.z - 1;

	PxI32 cellIndex = x + resolution.x * y + (resolution.x * resolution.y) * z;

	if ((occupiedCellBits[cellIndex >> 5] & (1u << (cellIndex & 31))) != 0)
	{
		SparseGridNode& n = sparseGrid3D[cellIndex];
		n.mExcessStartIndex++;
		return true;
	}
	else
	{
		sparseGrid3D.insert(cellIndex, SparseGridNode(pointIndex));
		occupiedCellBits[cellIndex >> 5] ^= (1u << (cellIndex & 31));
		return true;
	}
}

bool PoissonSamplerShared::addPointToSparseGrid(const PxVec3& p, PxI32 pointIndex)
{
	PxI32 x = PxI32((p.x - min.x) / cellSize);
	PxI32 y = PxI32((p.y - min.y) / cellSize);
	PxI32 z = PxI32((p.z - min.z) / cellSize);
	if (x >= resolution.x) x = resolution.x - 1;
	if (y >= resolution.y) y = resolution.y - 1;
	if (z >= resolution.z) z = resolution.z - 1;

	PxI32 cellIndex = x + resolution.x * y + (resolution.x * resolution.y) * z;

	//if (sparseGrid3D.ContainsKey(cellIndex))
	//    return false;

	sparseGrid3D.insert(cellIndex, pointIndex);
	occupiedCellBits[cellIndex >> 5] ^= (1u << (cellIndex & 31));
	return true;
}

PxReal PoissonSamplerShared::minDistanceToOtherSamplesSquared(const PxVec3& p) const
{
	PxI32 x = PxI32((p.x - min.x) / cellSize);
	PxI32 y = PxI32((p.y - min.y) / cellSize);
	PxI32 z = PxI32((p.z - min.z) / cellSize);
	if (x >= resolution.x) x = resolution.x - 1;
	if (y >= resolution.y) y = resolution.y - 1;
	if (z >= resolution.z) z = resolution.z - 1;

	PxReal minDist = FLT_MAX;

	for (PxI32 oX = -2; oX <= 2; ++oX)
	{
		for (PxI32 oY = -2; oY <= 2; ++oY)
		{
			for (PxI32 oZ = -2; oZ <= 2; ++oZ)
			{
				const PxI32 xx = x + oX;
				const PxI32 yy = y + oY;
				const PxI32 zz = z + oZ;

				if (xx >= 0 && xx < resolution.x && yy >= 0 && yy < resolution.y && zz >= 0 && zz < resolution.z)
				{
					PxI32 cellIndex = xx + resolution.x * yy + (resolution.x * resolution.y) * zz;
					if ((occupiedCellBits[cellIndex >> 5] & (1u << (cellIndex & 31))) != 0)
					{
						const PxPair<const PxI32, SparseGridNode>* it = sparseGrid3D.find(cellIndex);
						if (it) 
						{
							const PxReal dist2 = (result[it->second.mPointIndex] - p).magnitudeSquared();
							if (dist2 < minDist)
								minDist = dist2;
							if (it->second.mExcessStartIndex >= 0)
							{
								for (PxI32 i = it->second.mExcessStartIndex; i < it->second.mExcessEndIndex; ++i)
								{
									const PxReal dist2_ = (result[excessList[i]] - p).magnitudeSquared();
									if (dist2_ < minDist)
										minDist = dist2_;
								}
							}
						}
					}
				}
			}
		}
	}

	return minDist;
}
	
static void buildTree(const PxU32* triangles, const PxU32 numTriangles, const PxVec3* points, PxArray<Gu::BVHNode>& tree, PxF32 enlargement = 1e-4f)
{
	//Computes a bounding box for every triangle in triangles
	Gu::AABBTreeBounds boxes;
	boxes.init(numTriangles);
	for (PxU32 i = 0; i < numTriangles; ++i)
	{
		const PxU32* tri = &triangles[3 * i];
		PxBounds3 box = PxBounds3::empty();
		box.include(points[tri[0]]);
		box.include(points[tri[1]]);
		box.include(points[tri[2]]);
		box.fattenFast(enlargement);
		boxes.getBounds()[i] = box;
	}

	Gu::buildAABBTree(numTriangles, boxes, tree);
}

bool TriangleMeshPoissonSampler::isPointInTriangleMesh(const PxVec3& p)
{
	if (tree.size() == 0)
	{
		//Lazy initialization
		buildTree(originalTriangles, numOriginalTriangles, originalPoints, tree);
		Gu::precomputeClusterInformation(tree.begin(), originalTriangles, numOriginalTriangles, originalPoints, clusters);
	}

	return pointInMesh(p);
}

void TriangleMeshPoissonSampler::addSamplesInBox(const PxBounds3& axisAlignedBox, const PxQuat& boxOrientation, bool createVolumeSamples)
{
	PointInOBBTester pointInOBB(axisAlignedBox.getCenter(), axisAlignedBox.getExtents(), boxOrientation);
	addSamplesInVolume(pointInOBB, axisAlignedBox.getCenter(), axisAlignedBox.getExtents().magnitude(), createVolumeSamples);
}

void TriangleMeshPoissonSampler::addSamplesInSphere(const PxVec3& sphereCenter, PxReal sphereRadius, bool createVolumeSamples)
{
	PointInSphereTester pointInSphere(sphereCenter, sphereRadius);
	addSamplesInVolume(pointInSphere, sphereCenter, sphereRadius, createVolumeSamples);
}

//Ideally the sphere center is located on the mesh's surface
void TriangleMeshPoissonSampler::addSamplesInVolume(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, bool volumeSamples)
{
	PxArray<PxU32> localActiveSamples;
	for (PxU32 i = 0; i < activeSamples.size();)
	{
		if (activeSamples[i].mIndex >= PxI32(poissonSamplerShared.result.size()))
		{
			activeSamples[i] = activeSamples[activeSamples.size() - 1];
			activeSamples.remove(activeSamples.size() - 1);
			continue;
		}

		if (pointInSphere(poissonSamplerShared.result[activeSamples[i].mIndex], sphereCenter, sphereRadius))
			localActiveSamples.pushBack(i);

		++i;
	}

	if (localActiveSamples.size() == 0)
	{
		const PxReal r = poissonSamplerShared.currentSamplingRadius;

		//Try to find a seed sample
		for (PxU32 i = 0; i < triangles.size(); i += 3)
		{
			PxVec3 p = (1.0f / 3.0f) * (points[triangles[i]] + points[triangles[i + 1]] + points[triangles[i + 2]]);
			PxReal triRadius = PxSqrt(PxMax((p - points[triangles[i]]).magnitudeSquared(), PxMax((p - points[triangles[i + 1]]).magnitudeSquared(), (p - points[triangles[i + 2]]).magnitudeSquared())));
			PxReal sum = triRadius + sphereRadius;
			if ((p - sphereCenter).magnitudeSquared() < sum * sum)
			{
				bool success = false;
				for (PxI32 j = 0; j < 30; ++j)
				{
					PxVec3 sample = randomPointOnTriangle(poissonSamplerShared.rnd, triangles.begin() + i, points.begin());
					if (poissonSamplerShared.minDistanceToOtherSamplesSquared(sample) > r * r)
					{
						if (pointInVolume.pointInVolume(sample))
						{
							PxI32 newSampleId = PxI32(poissonSamplerShared.result.size());
							PxU32 sampleTriId = i / 3;

							ActiveSample as;
							createActiveSample(triangleAreaBuffer, newSampleId, sample, 2 * r, sampleTriId, triangles.begin(), points.begin(), adj, as);
							localActiveSamples.pushBack(activeSamples.size());
							activeSamples.pushBack(as);

							poissonSamplerShared.result.pushBack(sample);
							triangleIds.pushBack(triangleMap[sampleTriId]);
							{
								const PxU32 triId = triangleMap[sampleTriId];
								const PxU32* origTri = &originalTriangles[3 * triId];
								barycentricCoordinates.pushBack(computeBarycentricCoordinates(sample, originalPoints[origTri[0]], originalPoints[origTri[1]], originalPoints[origTri[2]]));
							}

							poissonSamplerShared.addPointToSparseGrid(sample, newSampleId);

							success = true;

							if (poissonSamplerShared.maxNumSamples > 0 && poissonSamplerShared.result.size() >= poissonSamplerShared.maxNumSamples)
								return;

							break;
						}
					}
				}
				if (success)
					break;
			}
		}
	}

	//Start poisson sampling
	while (localActiveSamples.size() > 0)
	{
		const PxReal r = poissonSamplerShared.currentSamplingRadius;

		PxI32 localSampleIndex = poissonSamplerShared.rnd.rand32() % localActiveSamples.size();
		PxI32 selectedActiveSample = localActiveSamples[localSampleIndex];
		const ActiveSample& s = activeSamples[selectedActiveSample];

		bool successDist = false;
		bool success = false;
		for (PxI32 i = 0; i < poissonSamplerShared.numSampleAttemptsAroundPoint; ++i)
		{
			PxI32 sampleTriId;
			PxVec3 barycentricCoordinate;
			PxVec3 sample;
			if (samplePointInBallOnSurface(poissonSamplerShared.rnd, s.mCumulativeTriangleAreas, points.begin(), triangles.begin(), s.mNearbyTriangles, poissonSamplerShared.result[s.mIndex], r, sample, sampleTriId, 30, &barycentricCoordinate))
			{
				if (poissonSamplerShared.minDistanceToOtherSamplesSquared(sample) > r * r)
				{
					successDist = true;
					if (pointInVolume.pointInVolume(sample))
					{
						PxI32 newSampleId = PxI32(poissonSamplerShared.result.size());

						ActiveSample as;
						createActiveSample(triangleAreaBuffer, newSampleId, sample, 2 * r, sampleTriId, triangles.begin(), points.begin(), adj, as);
						localActiveSamples.pushBack(activeSamples.size());
						activeSamples.pushBack(as);

						poissonSamplerShared.result.pushBack(sample);
						triangleIds.pushBack(triangleMap[sampleTriId]);
						{
							const PxU32 triId = triangleMap[sampleTriId];
							const PxU32* origTri = &originalTriangles[3 * triId];
							barycentricCoordinates.pushBack(computeBarycentricCoordinates(sample, originalPoints[origTri[0]], originalPoints[origTri[1]], originalPoints[origTri[2]]));
						}

						poissonSamplerShared.addPointToSparseGrid(sample, newSampleId);
						success = true;

						if (poissonSamplerShared.maxNumSamples > 0 && poissonSamplerShared.result.size() >= poissonSamplerShared.maxNumSamples)
							return;

						break;
					}
				}
			}
		}
		if (!successDist)
		{
			PxU32 oldId = activeSamples.size() - 1;
			activeSamples[selectedActiveSample] = activeSamples[activeSamples.size() - 1];
			activeSamples.remove(activeSamples.size() - 1);

			for (PxU32 i = 0; i < localActiveSamples.size(); ++i)
			{
				if (localActiveSamples[i] == oldId)
				{
					localActiveSamples[i] = selectedActiveSample;
					break;
				}
			}
		}
		if (!success)
		{
			localActiveSamples[localSampleIndex] = localActiveSamples[localActiveSamples.size() - 1];
			localActiveSamples.remove(localActiveSamples.size() - 1);
		}
	}		

	if (volumeSamples)
	{
		PxReal randomScale = 0.1f; //Relative to particleSpacing
		PxReal r = (1.0f + 2.0f * randomScale) * 1.001f * poissonSamplerShared.currentSamplingRadius;
		createVolumeSamples(pointInVolume, sphereCenter, sphereRadius, randomScale, r);
	}
}

void TriangleMeshPoissonSampler::createVolumeSamples(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, PxReal randomScale, PxReal r, bool addToSparseGrid)
{
	if (tree.size() == 0)
	{
		//Lazy initialization
		buildTree(originalTriangles, numOriginalTriangles, originalPoints, tree);
		Gu::precomputeClusterInformation(tree.begin(), originalTriangles, numOriginalTriangles, originalPoints, clusters);
	}

		
	PxVec3 sphereBoxMin = PxVec3(sphereCenter.x - sphereRadius, sphereCenter.y - sphereRadius, sphereCenter.z - sphereRadius) - poissonSamplerShared.min;
	PxVec3 sphereBoxMax = PxVec3(sphereCenter.x + sphereRadius, sphereCenter.y + sphereRadius, sphereCenter.z + sphereRadius) - poissonSamplerShared.min;

	Int3 start(PxI32(PxFloor(sphereBoxMin.x / r)), PxI32(PxFloor(sphereBoxMin.y / r)), PxI32(PxFloor(sphereBoxMin.z / r)));
	Int3 end(PxI32(PxCeil(sphereBoxMax.x / r)), PxI32(PxCeil(sphereBoxMax.y / r)), PxI32(PxCeil(sphereBoxMax.z / r)));

	for (PxI32 x = start.x; x < end.x; ++x)
	{
		for (PxI32 y = start.y; y < end.y; ++y)
		{
			for (PxI32 z = start.z; z < end.z; ++z)
			{
				PxVec3 p = poissonSamplerShared.min + PxVec3(x * r, y * r, z * r);
				p += PxVec3(poissonSamplerShared.rnd.randomFloat32(-randomScale, randomScale) * poissonSamplerShared.currentSamplingRadius,
					poissonSamplerShared.rnd.randomFloat32(-randomScale, randomScale) * poissonSamplerShared.currentSamplingRadius,
					poissonSamplerShared.rnd.randomFloat32(-randomScale, randomScale) * poissonSamplerShared.currentSamplingRadius);
				if (pointInVolume.pointInVolume(p))
				{
					if (poissonSamplerShared.minDistanceToOtherSamplesSquared(p) > poissonSamplerShared.currentSamplingRadius * poissonSamplerShared.currentSamplingRadius && pointInMesh(p))
					{
						PxI32 newSampleId = PxI32(poissonSamplerShared.result.size());
						poissonSamplerShared.result.pushBack(p);

						if (addToSparseGrid)
							poissonSamplerShared.addPointToSparseGrid(p, newSampleId);

						triangleIds.pushBack(-1);
						barycentricCoordinates.pushBack(PxVec3(0.0f));

						if (poissonSamplerShared.maxNumSamples > 0 && poissonSamplerShared.result.size() >= poissonSamplerShared.maxNumSamples)
							return;
					}
				}
			}
		}
	}
}

static PxU32 PX_FORCE_INLINE raycast(const PxGeometry& geometry, const PxTransform& transform,
	const PxVec3& rayOrigin, const PxVec3& rayDir, PxReal maxDist, PxHitFlags hitFlags,
	PxU32 maxHits, PxRaycastHit* rayHits)
{
	return PxGeometryQuery::raycast(
		rayOrigin, rayDir, geometry, transform, maxDist, hitFlags, maxHits, rayHits);
}

static bool PX_FORCE_INLINE pointInShape(const PxGeometry& geometry, const PxTransform& transform, const PxVec3& p)
{
	//This is a bit a work-around solution: When a raycast starts inside of a shape, it returns the ray origin as hit point
	PxRaycastHit hit;
	return PxGeometryQuery::raycast(p, PxVec3(1.0f, 0.0f, 0.0f), geometry, transform,
		1.0f, PxHitFlag::ePOSITION, 1, &hit) > 0 && hit.position == p;
}

static bool projectionCallback(PxReal targetDistance, const PxGeometry& geometry, const PxTransform& transform, PxReal boundingBoxDiagonalLength, const PxVec3& p, const PxVec3& n, PointWithNormal& result)
{
	PxRaycastHit hitPos;
	PxU32 numHitsPos = raycast(geometry, transform, p - boundingBoxDiagonalLength * n, n, 2 * boundingBoxDiagonalLength, PxHitFlag::eMESH_BOTH_SIDES | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL, 1, &hitPos);

	PxRaycastHit hitNeg;
	PxU32 numHitsNeg = raycast(geometry, transform, p + boundingBoxDiagonalLength * n, -n, 2 * boundingBoxDiagonalLength, PxHitFlag::eMESH_BOTH_SIDES | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL, 1, &hitNeg);

	targetDistance *= 0.5f;

	if (numHitsPos && numHitsNeg)
	{
		if (PxAbs((hitPos.position - p).magnitude() - targetDistance) < PxAbs((hitNeg.position - p).magnitude() - targetDistance))
			result = PointWithNormal(hitPos.position, hitPos.normal);
		else
			result = PointWithNormal(hitNeg.position, hitNeg.normal);
		return true;
	}
	else if (numHitsPos)
	{
		result = PointWithNormal(hitPos.position, hitPos.normal);
		return true;
	}
	else if (numHitsNeg)
	{
		result = PointWithNormal(hitNeg.position, hitNeg.normal);
		return true;
	}
	return false;
}

static PxVec3 randomDirection(BasicRandom& rnd)
{
	PxVec3 dir;
	do
	{
		dir = PxVec3((rnd.rand(0.0f, 1.0f) - 0.5f), (rnd.rand(0.0f, 1.0f) - 0.5f), (rnd.rand(0.0f, 1.0f) - 0.5f));
	} while (dir.magnitudeSquared() < 1e-8f);
	return dir.getNormalized();
}

static PxVec3 getPerpendicularVectorNormalized(const PxVec3& dir)
{
	PxReal x = PxAbs(dir.x);
	PxReal y = PxAbs(dir.y);
	PxReal z = PxAbs(dir.z);
	if (x >= y && x >= z)
		return dir.cross(PxVec3(0, 1, 0)).getNormalized();
	else if (y >= x && y >= z)
		return dir.cross(PxVec3(0, 0, 1)).getNormalized();
	else
		return dir.cross(PxVec3(1, 0, 0)).getNormalized();
}

static PxVec3 randomPointOnDisc(BasicRandom& rnd, const PxVec3& point, const PxVec3& normal, PxReal radius, PxReal rUpper)
{
	//TODO: Use better random number generator
	PxReal r = radius + rnd.rand(0.0f, 1.0f) * (rUpper - radius);
	PxVec3 x = getPerpendicularVectorNormalized(normal);
	PxVec3 y = normal.cross(x).getNormalized();
	PxReal angle = rnd.rand(0.0f, 1.0f) * (2.0f * 3.1415926535898f);
	return point + x * (r * PxCos(angle)) + y * (r * PxSin(angle));
}

static bool samplePointInBallOnSurface(BasicRandom& rnd, const PxGeometry& shape, const PxTransform& actorGlobalPose, const PxReal diagonalLength,
	const PxVec3& point, const PxVec3& normal, PxReal radius, PointWithNormal& sample, PxI32 numAttempts = 30)
{
	for (PxI32 i = 0; i < numAttempts; ++i) {
		PxVec3 p = randomPointOnDisc(rnd, point, normal, radius, 2 * radius);

		//Distort the direction of the normal a bit
		PxVec3 n = normal;
		do
		{
			n.x += 0.5f * (rnd.rand(0.0f, 1.0f) - 0.5f);
			n.y += 0.5f * (rnd.rand(0.0f, 1.0f) - 0.5f);
			n.z += 0.5f * (rnd.rand(0.0f, 1.0f) - 0.5f);
		} while (n.magnitudeSquared() < 1e-8f);

		n.normalize();
			
		if (projectionCallback(radius, shape, actorGlobalPose, diagonalLength, p, n, sample)) 
		{
			PxReal d2 = (sample.mPoint - point).magnitudeSquared();
			if (d2 >= radius * radius && d2 < 4 * radius * radius)
				return true;
		}
	}
	return false;
}

void ShapePoissonSampler::addSamplesInBox(const PxBounds3& axisAlignedBox, const PxQuat& boxOrientation, bool createVolumeSamples)
{
	PointInOBBTester pointInOBB(axisAlignedBox.getCenter(), axisAlignedBox.getExtents(), boxOrientation);
	addSamplesInVolume(pointInOBB, axisAlignedBox.getCenter(), axisAlignedBox.getExtents().magnitude(), createVolumeSamples);
}

void ShapePoissonSampler::addSamplesInSphere(const PxVec3& sphereCenter, PxReal sphereRadius, bool createVolumeSamples)
{
	PointInSphereTester pointInSphere(sphereCenter, sphereRadius);
	addSamplesInVolume(pointInSphere, sphereCenter, sphereRadius, createVolumeSamples);
}

void ShapePoissonSampler::addSamplesInVolume(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, bool volumeSamples)
{
	PxReal boundingBoxDiagonalLength = poissonSamplerShared.size.magnitude();
		
	PxArray<PxU32> localActiveSamples;
	for (PxU32 i = 0; i < activeSamples.size();)
	{
		if (activeSamples[i].mIndex >= PxI32(poissonSamplerShared.result.size()))
		{
			activeSamples[i] = activeSamples[activeSamples.size() - 1];
			activeSamples.remove(activeSamples.size() - 1);
			continue;
		}

		if (pointInSphere(poissonSamplerShared.result[activeSamples[i].mIndex], sphereCenter, sphereRadius))
			localActiveSamples.pushBack(i);

		++i;
	}

	if (localActiveSamples.size() == 0)
	{
		PxVec3 center = poissonSamplerShared.min + 0.5f * poissonSamplerShared.size;
		PointWithNormal sample;
		PxVec3 arbitrarySeedPointOnSurface;
			
		PxVec3 reference = sphereCenter - center;
		reference.normalizeSafe();

			
		for (PxI32 i = 0; i < 1000; ++i)
		{
			PxVec3 dir = /*reference + 0.5f**/randomDirection(poissonSamplerShared.rnd);
			dir.normalize();
			if (projectionCallback(poissonSamplerShared.currentSamplingRadius, shape, actorGlobalPose, boundingBoxDiagonalLength, sphereCenter, dir, sample))
			{
				if (poissonSamplerShared.minDistanceToOtherSamplesSquared(sample.mPoint) > poissonSamplerShared.currentSamplingRadius * poissonSamplerShared.currentSamplingRadius)
				{
					if (pointInVolume.pointInVolume(sample.mPoint))
					{
						PxI32 newSampleId = PxI32(poissonSamplerShared.result.size());

						localActiveSamples.pushBack(activeSamples.size());
						activeSamples.pushBack(IndexWithNormal(newSampleId, sample.mNormal));

						poissonSamplerShared.result.pushBack(sample.mPoint);
						poissonSamplerShared.addPointToSparseGrid(sample.mPoint, newSampleId);
							
						if (poissonSamplerShared.maxNumSamples > 0 && poissonSamplerShared.result.size() >= poissonSamplerShared.maxNumSamples)
							return;

						break;
					}
				}
			}
		}
	}

	while (localActiveSamples.size() > 0)
	{
		PxI32 localSampleIndex = poissonSamplerShared.rnd.rand32() % localActiveSamples.size();
		PxI32 selectedActiveSample = localActiveSamples[localSampleIndex];
		const IndexWithNormal& s = activeSamples[selectedActiveSample];
			
		bool successDist = false;
		bool success = false;
		for (PxI32 i = 0; i < poissonSamplerShared.numSampleAttemptsAroundPoint; ++i)
		{
			PointWithNormal sample;
			if (samplePointInBallOnSurface(poissonSamplerShared.rnd, shape, actorGlobalPose, boundingBoxDiagonalLength, poissonSamplerShared.result[s.mIndex], s.mNormal, poissonSamplerShared.currentSamplingRadius, sample))
			{
				if (poissonSamplerShared.minDistanceToOtherSamplesSquared(sample.mPoint) > poissonSamplerShared.currentSamplingRadius * poissonSamplerShared.currentSamplingRadius)
				{
					successDist = true;
					if (pointInVolume.pointInVolume(sample.mPoint))
					{
						successDist = true;

						PxI32 newSampleId = PxI32(poissonSamplerShared.result.size());
						localActiveSamples.pushBack(activeSamples.size());
						activeSamples.pushBack(IndexWithNormal(newSampleId, sample.mNormal));

						poissonSamplerShared.result.pushBack(sample.mPoint);
						poissonSamplerShared.addPointToSparseGrid(sample.mPoint, newSampleId);
						success = true;

						if (poissonSamplerShared.maxNumSamples > 0 && poissonSamplerShared.result.size() >= poissonSamplerShared.maxNumSamples)
							return;

						break;
					}
				}
			}
		}
		if (!successDist)
		{
			PxU32 oldId = activeSamples.size() - 1;
			activeSamples[selectedActiveSample] = activeSamples[activeSamples.size() - 1];
			activeSamples.remove(activeSamples.size() - 1);

			for (PxU32 i = 0; i < localActiveSamples.size(); ++i)
			{
				if (localActiveSamples[i] == oldId)
				{
					localActiveSamples[i] = selectedActiveSample;
					break;
				}
			}
		}
		if (!success)
		{
			localActiveSamples[localSampleIndex] = localActiveSamples[localActiveSamples.size() - 1];
			localActiveSamples.remove(localActiveSamples.size() - 1);
		}
	}
		
	if (volumeSamples)
	{
		PxReal randomScale = 0.1f; //Relative to particleSpacing
		PxReal r = (1.0f + 2.0f * randomScale) * 1.001f * poissonSamplerShared.currentSamplingRadius;
		createVolumeSamples(pointInVolume, sphereCenter, sphereRadius, randomScale, r);
	}
}	

void ShapePoissonSampler::createVolumeSamples(const PointInVolumeTester& pointInVolume, const PxVec3& sphereCenter, PxReal sphereRadius, PxReal randomScale, PxReal r, bool addToSparseGrid)
{
	PxVec3 sphereBoxMin = PxVec3(sphereCenter.x - sphereRadius, sphereCenter.y - sphereRadius, sphereCenter.z - sphereRadius) - poissonSamplerShared.min;
	PxVec3 sphereBoxMax = PxVec3(sphereCenter.x + sphereRadius, sphereCenter.y + sphereRadius, sphereCenter.z + sphereRadius) - poissonSamplerShared.min;

	Int3 start(PxI32(PxFloor(sphereBoxMin.x / r)), PxI32(PxFloor(sphereBoxMin.y / r)), PxI32(PxFloor(sphereBoxMin.z / r)));
	Int3 end(PxI32(PxCeil(sphereBoxMax.x / r)), PxI32(PxCeil(sphereBoxMax.y / r)), PxI32(PxCeil(sphereBoxMax.z / r)));

	for (PxI32 x = start.x; x < end.x; ++x)
	{
		for (PxI32 y = start.y; y < end.y; ++y)
		{
			for (PxI32 z = start.z; z < end.z; ++z)
			{
				PxVec3 p = poissonSamplerShared.min + PxVec3(x * r, y * r, z * r);
				p += PxVec3(poissonSamplerShared.rnd.randomFloat32(-randomScale, randomScale) * poissonSamplerShared.currentSamplingRadius,
					poissonSamplerShared.rnd.randomFloat32(-randomScale, randomScale) * poissonSamplerShared.currentSamplingRadius,
					poissonSamplerShared.rnd.randomFloat32(-randomScale, randomScale) * poissonSamplerShared.currentSamplingRadius);
				if (pointInVolume.pointInVolume(p) && pointInShape(shape, actorGlobalPose, p))
				{
					if (poissonSamplerShared.minDistanceToOtherSamplesSquared(p) > poissonSamplerShared.currentSamplingRadius * poissonSamplerShared.currentSamplingRadius)
					{
						PxI32 newSampleId = PxI32(poissonSamplerShared.result.size());
						poissonSamplerShared.result.pushBack(p);
							
						if (addToSparseGrid)
							poissonSamplerShared.addPointToSparseGrid(p, newSampleId);

						if (poissonSamplerShared.maxNumSamples > 0 && poissonSamplerShared.result.size() >= poissonSamplerShared.maxNumSamples)
							return;
					}
				}
			}
		}
	}
}

//Use for triangle meshes
//https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
bool PxSamplingExt::poissonSample(const PxSimpleTriangleMesh& mesh, PxReal r, PxArray<PxVec3>& result, PxReal rVolume, PxArray<PxI32>* triangleIds, PxArray<PxVec3>* barycentricCoordinates,
	const PxBounds3* axisAlignedBox, const PxQuat* boxOrientation, PxU32 maxNumSamples, PxU32 numSampleAttemptsAroundPoint)
{
	TriangleMeshPoissonSampler sampler(reinterpret_cast<const PxU32*>(mesh.triangles.data), mesh.triangles.count, reinterpret_cast<const PxVec3*>(mesh.points.data), mesh.points.count, r, numSampleAttemptsAroundPoint, maxNumSamples);

	if (!sampler.poissonSamplerShared.gridResolutionValid)
	{
		return false;
	}

	PxVec3 center = 0.5f*(sampler.max + sampler.poissonSamplerShared.min);
	PxReal boundingSphereRadius = 1.001f * (sampler.max - sampler.poissonSamplerShared.min).magnitude() * 0.5f;

	if (axisAlignedBox == NULL || boxOrientation == NULL) 
	{			
		sampler.addSamplesInSphere(center, boundingSphereRadius, false);

		if (rVolume > 0.0f)
		{
			AlwaysInsideTester tester;
			sampler.createVolumeSamples(tester, center, boundingSphereRadius, 0.1f, rVolume, false);
		}
	}
	else
	{
		sampler.addSamplesInBox(*axisAlignedBox, *boxOrientation, false);

		if (rVolume > 0.0f)
		{
			PointInOBBTester tester(axisAlignedBox->getCenter(), axisAlignedBox->getExtents(), *boxOrientation);
			sampler.createVolumeSamples(tester, center, boundingSphereRadius, 0.1f, rVolume, false);
		}
	}

	result = sampler.getSamples();
	if (triangleIds)
		*triangleIds = sampler.getSampleTriangleIds();
	if (barycentricCoordinates)
		*barycentricCoordinates = sampler.getSampleBarycentrics();

	return true;
}	

bool PxSamplingExt::poissonSample(const PxGeometry& geometry, const PxTransform& transform, const PxBounds3& worldBounds, PxReal r, PxArray<PxVec3>& result, PxReal rVolume,
	const PxBounds3* axisAlignedBox, const PxQuat* boxOrientation, PxU32 maxNumSamples, PxU32 numSampleAttemptsAroundPoint)
{
	PxVec3 center = worldBounds.getCenter();

	ShapePoissonSampler sampler(geometry, transform, worldBounds, r, numSampleAttemptsAroundPoint, maxNumSamples);
	PxReal boundingSphereRadius = 1.001f * worldBounds.getExtents().magnitude();

	if (!sampler.poissonSamplerShared.gridResolutionValid)
		return false;

	if (axisAlignedBox == NULL || boxOrientation == NULL)
	{
		sampler.addSamplesInSphere(center, worldBounds.getExtents().magnitude() * 1.001f, false);

		if (rVolume > 0.0f)
		{				
			AlwaysInsideTester tester;
			sampler.createVolumeSamples(tester, center, boundingSphereRadius, 0.1f, rVolume, false);
		}
	}
	else
	{
		sampler.addSamplesInBox(*axisAlignedBox, *boxOrientation, false);

		if (rVolume > 0.0f)
		{				
			PointInOBBTester tester(axisAlignedBox->getCenter(), axisAlignedBox->getExtents(), *boxOrientation);
			sampler.createVolumeSamples(tester, center, boundingSphereRadius, 0.1f, rVolume, false);
		}
	}

	result = sampler.getSamples();
	return true;
}
