// 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.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.  

#ifndef PX_ISOSURFACE_EXTRACTION_H
#define PX_ISOSURFACE_EXTRACTION_H


#include "cudamanager/PxCudaContext.h"
#include "cudamanager/PxCudaContextManager.h"

#include "foundation/PxSimpleTypes.h"
#include "foundation/PxVec4.h"
#include "PxParticleSystem.h"

#include "PxSparseGridParams.h"

#include "foundation/PxArray.h"
#include "PxParticleGpu.h"

#if !PX_DOXYGEN
namespace physx
{
#endif
	
#if PX_SUPPORT_GPU_PHYSX
		
	/**
	\brief Identifies filter type to be applied on the isosurface grid.
	*/
	struct PxIsosurfaceGridFilteringType
	{
		enum Enum
		{
			eNONE = 0,		//!< No filtering
			eSMOOTH = 1,	//!< Gaussian-blur-like filtering
			eGROW = 2,		//!< A dilate/erode operation will be applied that makes the fluid grow approximately one cell size
			eSHRINK = 3	//!< A dilate/erode operation will be applied that makes the fluid shrink approximately one cell size
		};
	};

	/**
	\brief Parameters to define the isosurface extraction settings like isosurface level, filtering etc.
	*/
	struct PxIsosurfaceParams
	{
		/**
		\brief Default constructor.
		*/
		PX_INLINE PxIsosurfaceParams()
		{
			particleCenterToIsosurfaceDistance = 0.2f;
			numMeshSmoothingPasses = 4;
			numMeshNormalSmoothingPasses = 4;
			gridFilteringFlags = 0;
			gridSmoothingRadius = 0.2f;
		}		

		/**
		\brief Clears the filtering operations
		*/
		PX_INLINE void clearFilteringPasses()
		{
			gridFilteringFlags = 0;
		}

		/**
		\brief Adds a smoothing pass after the existing ones

		At most 32 smoothing passes can be defined. Every pass can repeat itself up to 4 times.

		\param[in] operation The smoothing operation to add
		\return The index of the smoothing pass that was added
		*/
		PX_INLINE PxU64 addGridFilteringPass(PxIsosurfaceGridFilteringType::Enum operation)
		{
			for (PxU32 i = 0; i < 32; ++i)
			{
				PxIsosurfaceGridFilteringType::Enum o;				
				if (!getGridFilteringPass(i, o))
				{
					setGridFilteringPass(i, operation);
					return i;
				}
			}
			return 0xFFFFFFFFFFFFFFFF;
		}

		/**
		\brief Sets the operation of a smoothing pass

		\param[in] passIndex The index of the smoothing pass whose operation should be set <b>Range:</b> [0, 31]
		\param[in] operation The operation the modified smoothing will perform
		*/
		PX_INLINE void setGridFilteringPass(PxU32 passIndex, PxIsosurfaceGridFilteringType::Enum operation)
		{
			PX_ASSERT(passIndex < 32u);
			PxU32 shift = passIndex * 2;
			gridFilteringFlags &= ~(PxU64(3) << shift);
			gridFilteringFlags |= PxU64(operation) << shift;
		}

		/**
		\brief Returns the operation of a smoothing pass

		\param[in] passIndex The index of the smoothing pass whose operation is requested <b>Range:</b> [0, 31]
		\param[out] operation The operation the requested smoothing pass will perform
		\return true if the requested pass performs an operation
		*/
		PX_INLINE bool getGridFilteringPass(PxU32 passIndex, PxIsosurfaceGridFilteringType::Enum& operation) const
		{
			PxU32 shift = passIndex * 2;
			PxU64 v = gridFilteringFlags >> shift;
			v &= 3; //Extract last 2 bits
			operation = PxIsosurfaceGridFilteringType::Enum(v);
			return operation != PxIsosurfaceGridFilteringType::eNONE;
		}

		PxReal	particleCenterToIsosurfaceDistance;			//!< Distance form a particle center to the isosurface
		PxU32	numMeshSmoothingPasses;						//!< Number of Taubin mesh postprocessing smoothing passes. Using an even number of passes lead to less shrinking.			
		PxU32	numMeshNormalSmoothingPasses;				//!< Number of mesh normal postprocessing smoothing passes.
		PxU64	gridFilteringFlags;							//!< Encodes the smoothing steps to apply on the sparse grid. Use setGridSmoothingPass method to set up.
		PxReal	gridSmoothingRadius;						//!< Gaussian blur smoothing kernel radius used for smoothing operations on the grid
	};

	/**
	\brief Base class for isosurface extractors. Allows to register the data arrays for the isosurface and to obtain the number vertices/triangles in use.
	*/
	class PxIsosurfaceExtractor
	{
	public:
		/**
		\brief Returns the isosurface parameters. 

		\return The isosurfacesettings used for the isosurface extraction
		*/
		virtual PxIsosurfaceParams getIsosurfaceParams() const = 0;

		/**
		\brief Set the isosurface extraction parameters

		Allows to configure the isosurface extraction by controlling threshold value, smoothing options etc.

		\param[in] params A collection of settings to control the isosurface extraction
		*/
		virtual void setIsosurfaceParams(const PxIsosurfaceParams& params) = 0;

		/**
		\brief Returns the number of vertices that the current isosurface triangle mesh uses

		\return The number of vertices currently in use
		*/
		virtual PxU32 getNumVertices() const = 0;
			
		/**
		\brief Returns the number of triangles that the current isosurface triangle mesh uses

		\return The number of triangles currently in use
		*/
		virtual PxU32 getNumTriangles() const = 0;

		/**
		\brief Returns the maximum number of vertices that the isosurface triangle mesh can contain

		\return The maximum number of vertices that can be genrated
		*/
		virtual PxU32 getMaxVertices() const = 0;

		/**
		\brief Returns the maximum number of triangles that the isosurface triangle mesh can contain

		\return The maximum number of triangles that can be generated
		*/
		virtual PxU32 getMaxTriangles() const = 0;

		/**
		\brief Resizes the internal triangle mesh buffers. 
			
		If the output buffers are device buffers, nothing will get resized but new output buffers can be set using setResultBufferDevice. 
		For host side output buffers, temporary buffers will get resized. The new host side result buffers with the same size must be set using setResultBufferHost.

		\param[in] maxNumVertices The maximum number of vertices the output buffer can hold
		\param[in] maxNumTriangles The maximum number of triangles the ouput buffer can hold
		*/
		virtual void setMaxVerticesAndTriangles(PxU32 maxNumVertices, PxU32 maxNumTriangles) = 0;

		/**
		\brief The maximal number of particles the isosurface extractor can process

		\return The maximal number of particles
		*/
		virtual PxU32 getMaxParticles() const = 0;

		/**
		\brief Sets the maximal number of particles the isosurface extractor can process

		\param[in] maxParticles The maximal number of particles
		*/
		virtual void setMaxParticles(PxU32 maxParticles) = 0;

		/**
		\brief Releases the isosurface extractor instance and its data
		*/
		virtual void release() = 0;

		/**
		\brief Triggers the compuation of a new isosurface based on the specified particle locations

		\param[in] deviceParticlePos A gpu pointer pointing to the start of the particle array
		\param[in] numParticles The number of particles
		\param[in] stream The stream on which all the gpu work will be performed
		\param[in] phases A phase value per particle
		\param[in] validPhaseMask A mask that specifies which phases should contribute to the isosurface. If the binary and operation 
		between this mask and the particle phase is non zero, then the particle will contribute to the isosurface
		\param[in] activeIndices Optional array with indices of all active particles
		\param[in] anisotropy1 Optional anisotropy information, x axis direction (xyz) and scale in w component
		\param[in] anisotropy2 Optional anisotropy information, y axis direction (xyz) and scale in w component
		\param[in] anisotropy3 Optional anisotropy information, z axis direction (xyz) and scale in w component
		\param[in] anisotropyFactor A factor to multiply with the anisotropy scale
		*/
		virtual void extractIsosurface(PxVec4* deviceParticlePos, const PxU32 numParticles, CUstream stream, PxU32* phases = NULL, PxU32 validPhaseMask = PxParticlePhaseFlag::eParticlePhaseFluid,
			PxU32* activeIndices = NULL, PxVec4* anisotropy1 = NULL, PxVec4* anisotropy2 = NULL, PxVec4* anisotropy3 = NULL, PxReal anisotropyFactor = 1.0f) = 0;

		/**
		\brief Allows to register the host buffers into which the final isosurface triangle mesh will get stored

		\param[in] vertices A host buffer to store the vertices of the isosurface mesh
		\param[in] triIndices A host buffer to store the triangles of the isosurface mesh
		\param[in] normals A host buffer to store the normals of the isosurface mesh
		*/
		virtual void setResultBufferHost(PxVec4* vertices, PxU32* triIndices, PxVec4* normals = NULL) = 0;

		/**
		\brief Allows to register the host buffers into which the final isosurface triangle mesh will get stored

		\param[in] vertices A device buffer to store the vertices of the isosurface mesh
		\param[in] triIndices A device buffer to store the triangles of the isosurface mesh
		\param[in] normals A device buffer to store the normals of the isosurface mesh
		*/
		virtual void setResultBufferDevice(PxVec4* vertices, PxU32* triIndices, PxVec4* normals) = 0;

		/**
		\brief Enables or disables the isosurface extractor

		\param[in] enabled The boolean to set the extractor to enabled or disabled
		*/
		virtual void setEnabled(bool enabled) = 0;

		/**
		\brief Allows to query if the isosurface extractor is enabled

		\return True if enabled, false otherwise
		*/
		virtual bool isEnabled() const = 0;

		/**
		\brief Destructor
		*/
		virtual ~PxIsosurfaceExtractor() {}
	};

	/**
	\brief Base class for sparse grid based isosurface extractors. Allows to register the data arrays for the isosurface and to obtain the number vertices/triangles in use.
	*/
	class PxSparseGridIsosurfaceExtractor : public PxIsosurfaceExtractor
	{
		/**
		\brief Returns the sparse grid parameters.

		\return The sparse grid settings used for the isosurface extraction
		*/
		virtual PxSparseGridParams getSparseGridParams() const = 0;

		/**
		\brief Set the sparse grid parameters

		Allows to configure cell size, number of subgrids etc.

		\param[in] params A collection of settings to control the isosurface grid
		*/
		virtual void setSparseGridParams(const PxSparseGridParams& params) = 0;
	};
		
	/**
	\brief Default implementation of a particle system callback to trigger the isosurface extraction. A call to fetchResultsParticleSystem() on the 
	PxScene will synchronize the work such that the caller knows that the post solve task completed.
	*/
	class PxIsosurfaceCallback : public PxParticleSystemCallback
	{		
	public:
		/**
		\brief Initializes the isosurface callback

		\param[in] isosurfaceExtractor The isosurface extractor
		\param[in] validPhaseMask The valid phase mask marking the phase bits that particles must have set in order to contribute to the isosurface
		*/
		void initialize(PxIsosurfaceExtractor* isosurfaceExtractor, PxU32 validPhaseMask = PxParticlePhaseFlag::eParticlePhaseFluid) 
		{
			mIsosurfaceExtractor = isosurfaceExtractor;
			mValidPhaseMask = validPhaseMask;
		}

		virtual void onPostSolve(const PxGpuMirroredPointer<PxGpuParticleSystem>& gpuParticleSystem, CUstream stream)
		{
			mIsosurfaceExtractor->extractIsosurface(reinterpret_cast<PxVec4*>(gpuParticleSystem.mHostPtr->mUnsortedPositions_InvMass),
				gpuParticleSystem.mHostPtr->mCommonData.mMaxParticles, stream, gpuParticleSystem.mHostPtr->mUnsortedPhaseArray, mValidPhaseMask);
		}

		virtual void onBegin(const PxGpuMirroredPointer<PxGpuParticleSystem>& /*gpuParticleSystem*/, CUstream /*stream*/) { }

		virtual void onAdvance(const PxGpuMirroredPointer<PxGpuParticleSystem>& /*gpuParticleSystem*/, CUstream /*stream*/) { }
		
	private:
		PxIsosurfaceExtractor* mIsosurfaceExtractor;
		PxU32 mValidPhaseMask;
	};

#endif	

#if !PX_DOXYGEN
} // namespace physx
#endif

#endif
