// 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 GU_TRIANGLEMESH_H
#define GU_TRIANGLEMESH_H

#include "foundation/PxIO.h"
#include "geometry/PxTriangle.h"
#include "geometry/PxTriangleMeshGeometry.h"
#include "geometry/PxSimpleTriangleMesh.h"
#include "geometry/PxTriangleMesh.h"

#include "GuMeshData.h"
#include "GuCenterExtents.h"
#include "CmScaling.h"
#include "CmRefCountable.h"
#include "common/PxRenderOutput.h"

namespace physx
{
class PxMeshScale;
struct PxTriangleMeshInternalData;

namespace Gu
{
	PX_FORCE_INLINE	void	getVertexRefs(PxU32 triangleIndex, PxU32& vref0, PxU32& vref1, PxU32& vref2, const void* indices, bool has16BitIndices)
	{
		if(has16BitIndices)
		{
			const PxU16* inds = reinterpret_cast<const PxU16*>(indices) + triangleIndex*3;
			vref0 = inds[0];
			vref1 = inds[1];
			vref2 = inds[2];
		} 
		else 
		{ 
			const PxU32* inds = reinterpret_cast<const PxU32*>(indices) + triangleIndex*3;
			vref0 = inds[0];
			vref1 = inds[1];
			vref2 = inds[2];
		} 
	}

class MeshFactory;
#if PX_VC
#pragma warning(push)
#pragma warning(disable: 4324)	// Padding was added at the end of a structure because of a __declspec(align) value.
#endif

class EdgeList;

class TriangleMesh : public PxTriangleMesh, public PxUserAllocated
{
public:

// PX_SERIALIZATION
														TriangleMesh(PxBaseFlags baseFlags)	: PxTriangleMesh(baseFlags), mSdfData(PxEmpty)	{}

								void					preExportDataReset() { Cm::RefCountable_preExportDataReset(*this); }
	virtual						void					exportExtraData(PxSerializationContext& context);
								void					importExtraData(PxDeserializationContext& context);
	virtual						void					release();
	virtual						void					requiresObjects(PxProcessPxBaseCallback&){}
//~PX_SERIALIZATION
														TriangleMesh(MeshFactory* factory, TriangleMeshData& data);
														TriangleMesh(const PxTriangleMeshInternalData& data);
	virtual												~TriangleMesh();
	
	// PxBase
	virtual						void					onRefCountZero();
	//~PxBase

	// PxRefCounted
	virtual						void					acquireReference()					{ Cm::RefCountable_incRefCount(*this);			}
	virtual						PxU32					getReferenceCount()			const	{ return Cm::RefCountable_getRefCount(*this);	}
	//~PxRefCounted
	
	// PxTriangleMesh
	virtual						PxU32					getNbVertices()				const   { return mNbVertices;}
	virtual						const PxVec3*			getVertices()				const   { return mVertices; }

	virtual						PxVec3*					getVerticesForModification();
	virtual						PxBounds3				refitBVH();
	virtual						PxU32					getNbTriangles()			const	{ return mNbTriangles;					}
	virtual						const void*				getTriangles()				const	{ return mTriangles;					}
	virtual						PxTriangleMeshFlags		getTriangleMeshFlags()		const	{ return PxTriangleMeshFlags(mFlags);	}
	virtual						const PxU32*			getTrianglesRemap()			const	{ return mFaceRemap;					}
	virtual						void					setPreferSDFProjection(bool preferProjection)
	{
		if (preferProjection)
			mFlags &= PxU8(~PxTriangleMeshFlag::ePREFER_NO_SDF_PROJ);
		else
			mFlags |= PxTriangleMeshFlag::ePREFER_NO_SDF_PROJ;
	}

	virtual						bool					getPreferSDFProjection()	const { return !(mFlags & PxTriangleMeshFlag::ePREFER_NO_SDF_PROJ); }

	virtual						PxMaterialTableIndex	getTriangleMaterialIndex(PxTriangleID triangleIndex)	const
														{
															return hasPerTriangleMaterials() ? getMaterials()[triangleIndex] : PxMaterialTableIndex(0xffff);
														}

	virtual						PxBounds3				getLocalBounds()			const
														{
															PX_ASSERT(mAABB.isValid());
															return PxBounds3::centerExtents(mAABB.mCenter, mAABB.mExtents);
														}

	virtual						const PxReal*			getSDF()					const
														{
															return mSdfData.mSdf;
														}

	virtual						void					getSDFDimensions(PxU32& numX, PxU32& numY, PxU32& numZ) const
														{
															if(mSdfData.mSdf)
															{
																numX = mSdfData.mDims.x;
																numY = mSdfData.mDims.y;
																numZ = mSdfData.mDims.z;
															}
															else
																numX = numY = numZ = 0;
														}

	virtual						void					getMassInformation(PxReal& mass, PxMat33& localInertia, PxVec3& localCenterOfMass)	const
														{
															mass = mMass; localInertia = mInertia; localCenterOfMass = mLocalCenterOfMass;
														}
	
	//~PxTriangleMesh

	virtual						bool					getInternalData(PxTriangleMeshInternalData&, bool)	const	{ return false; }

	// PT: this one is just to prevent instancing Gu::TriangleMesh.
	// But you should use PxBase::getConcreteType() instead to avoid the virtual call.
	virtual						PxMeshMidPhase::Enum	getMidphaseID()				const	= 0;

	PX_FORCE_INLINE				const PxU32*			getFaceRemap()				const	{ return mFaceRemap;											}
	PX_FORCE_INLINE				bool					has16BitIndices()			const	{ return (mFlags & PxMeshFlag::e16_BIT_INDICES) ? true : false;	}
	PX_FORCE_INLINE				bool					hasPerTriangleMaterials()	const	{ return mMaterialIndices != NULL;								}
	PX_FORCE_INLINE				PxU32					getNbVerticesFast()			const	{ return mNbVertices;		}
	PX_FORCE_INLINE				PxU32					getNbTrianglesFast()		const	{ return mNbTriangles;		}
	PX_FORCE_INLINE				const void*				getTrianglesFast()			const	{ return mTriangles;		}
	PX_FORCE_INLINE				const PxVec3*			getVerticesFast()			const	{ return mVertices;			}
	PX_FORCE_INLINE				const PxU32*			getAdjacencies()			const	{ return mAdjacencies;		}
	PX_FORCE_INLINE				PxReal					getGeomEpsilon()			const	{ return mGeomEpsilon;		}
	PX_FORCE_INLINE				const CenterExtents&	getLocalBoundsFast()		const	{ return mAABB;				}
	PX_FORCE_INLINE				const PxU16*			getMaterials()				const	{ return mMaterialIndices;	}
	PX_FORCE_INLINE				const PxU8*				getExtraTrigData()			const	{ return mExtraTrigData;	}
	
	PX_FORCE_INLINE				const PxU32*			getAccumulatedTriangleRef()	const	{ return mAccumulatedTrianglesRef;	}
	PX_FORCE_INLINE				const PxU32*			getTriangleReferences()		const	{ return mTrianglesReferences;		}
	PX_FORCE_INLINE				PxU32					getNbTriangleReferences()	const	{ return mNbTrianglesReferences;	}

	PX_FORCE_INLINE			const CenterExtentsPadded&	getPaddedBounds()			const
														{
															// PT: see compile-time assert in cpp
															return static_cast<const CenterExtentsPadded&>(mAABB);
														}

	PX_FORCE_INLINE				void					computeWorldTriangle(
															PxTriangle& worldTri, PxTriangleID triangleIndex, const PxMat34& worldMatrix, bool flipNormal = false,
															PxU32* PX_RESTRICT vertexIndices=NULL, PxU32* PX_RESTRICT adjacencyIndices=NULL) const;
	PX_FORCE_INLINE				void					getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal = false) const;

								void					setMeshFactory(MeshFactory* factory) { mMeshFactory = factory; }

								// SDF methods
	PX_FORCE_INLINE				const SDF&				getSdfDataFast()			const { return mSdfData; }
								//~SDF methods

	PX_FORCE_INLINE				PxReal					getMass()			const { return mMass; }

								// PT: for debug viz
	PX_PHYSX_COMMON_API			const Gu::EdgeList*		requestEdgeList()			const;

protected:
								PxU32					mNbVertices;
								PxU32					mNbTriangles;
								PxVec3*					mVertices;
								void*					mTriangles;				//!< 16 (<= 0xffff #vertices) or 32 bit trig indices (mNbTriangles * 3)
								// 16 bytes block

		// PT: WARNING: bounds must be followed by at least 32bits of data for safe SIMD loading
								CenterExtents			mAABB;
								PxU8*					mExtraTrigData;			//one per trig
								PxReal					mGeomEpsilon;			//!< see comments in cooking code referencing this variable
								// 16 bytes block
		/*
		low 3 bits (mask: 7) are the edge flags:
		b001 = 1 = ignore edge 0 = edge v0-->v1
		b010 = 2 = ignore edge 1 = edge v0-->v2
		b100 = 4 = ignore edge 2 = edge v1-->v2
		*/
								PxU8					mFlags;					//!< Flag whether indices are 16 or 32 bits wide
																				//!< Flag whether triangle adajacencies are build
								PxU16*					mMaterialIndices;		//!< the size of the array is numTriangles.
								PxU32*					mFaceRemap;				//!< new faces to old faces mapping (after cleaning, etc). Usage: old = faceRemap[new]
								PxU32*					mAdjacencies;			//!< Adjacency information for each face - 3 adjacent faces
																				//!< Set to 0xFFFFffff if no adjacent face
	
								MeshFactory*			mMeshFactory;			// PT: changed to pointer for serialization
				mutable			Gu::EdgeList*			mEdgeList;				// PT: for debug viz


								PxReal					mMass;					//this is mass assuming a unit density that can be scaled by instances!
								PxMat33					mInertia;				//in local space of mesh!
								PxVec3					mLocalCenterOfMass;		//local space com
public:
								
								// GRB data -------------------------
								void*					mGRB_triIndices;		//!< GRB: GPU-friendly tri indices

								// TODO avoroshilov: cooking - adjacency info - duplicated, remove it and use 'mAdjacencies' and 'mExtraTrigData' see GuTriangleMesh.cpp:325
								void*					mGRB_triAdjacencies;	//!< GRB: adjacency data, with BOUNDARY and NONCONVEX flags (flags replace adj indices where applicable)

								PxU32*					mGRB_faceRemap;			//!< GRB : gpu to cpu triangle indice remap
								PxU32*					mGRB_faceRemapInverse;
								Gu::BV32Tree*			mGRB_BV32Tree;			//!< GRB: BV32 tree
								// End of GRB data ------------------

								// SDF data -------------------------
								SDF						mSdfData;
								// End of SDF data ------------------

								void					setAllEdgesActive();
								//Vertex mapping data
								PxU32*					mAccumulatedTrianglesRef;//runsum
								PxU32*					mTrianglesReferences;
								PxU32					mNbTrianglesReferences;
								//End of vertex mapping data
};

#if PX_VC
#pragma warning(pop)
#endif

} // namespace Gu

PX_FORCE_INLINE void Gu::TriangleMesh::computeWorldTriangle(PxTriangle& worldTri, PxTriangleID triangleIndex, const PxMat34& worldMatrix, bool flipNormal,
	PxU32* PX_RESTRICT vertexIndices, PxU32* PX_RESTRICT adjacencyIndices) const
{
	PxU32 vref0, vref1, vref2;
	getVertexRefs(triangleIndex, vref0, vref1, vref2, mTriangles, has16BitIndices());

	if(flipNormal)
		PxSwap<PxU32>(vref1, vref2);

	const PxVec3* PX_RESTRICT vertices = getVerticesFast();
	worldTri.verts[0] = worldMatrix.transform(vertices[vref0]);
	worldTri.verts[1] = worldMatrix.transform(vertices[vref1]);
	worldTri.verts[2] = worldMatrix.transform(vertices[vref2]);

	if(vertexIndices)
	{
		vertexIndices[0] = vref0;
		vertexIndices[1] = vref1;
		vertexIndices[2] = vref2;
	}

	if(adjacencyIndices)
	{
		if(mAdjacencies)
		{
			// PT: TODO: is this correct?
			adjacencyIndices[0] = flipNormal ? mAdjacencies[triangleIndex*3 + 2] : mAdjacencies[triangleIndex*3 + 0];
			adjacencyIndices[1] = mAdjacencies[triangleIndex*3 + 1];
			adjacencyIndices[2] = flipNormal ? mAdjacencies[triangleIndex*3 + 0] : mAdjacencies[triangleIndex*3 + 2];
		}
		else
		{
			adjacencyIndices[0] = 0xffffffff;
			adjacencyIndices[1] = 0xffffffff;
			adjacencyIndices[2] = 0xffffffff;
		}
	}
}

PX_FORCE_INLINE void Gu::TriangleMesh::getLocalTriangle(PxTriangle& localTri, PxTriangleID triangleIndex, bool flipNormal) const
{
	PxU32 vref0, vref1, vref2;
	getVertexRefs(triangleIndex, vref0, vref1, vref2, mTriangles, has16BitIndices());

	if(flipNormal)
		PxSwap<PxU32>(vref1, vref2);

	const PxVec3* PX_RESTRICT vertices = getVerticesFast();
	localTri.verts[0] = vertices[vref0];
	localTri.verts[1] = vertices[vref1];
	localTri.verts[2] = vertices[vref2];
}

PX_INLINE float computeSweepData(const PxTriangleMeshGeometry& triMeshGeom, /*const Cm::FastVertex2ShapeScaling& scaling,*/ PxVec3& sweepOrigin, PxVec3& sweepExtents, PxVec3& sweepDir, float distance)
{
	PX_ASSERT(!Cm::isEmpty(sweepOrigin, sweepExtents));

	const PxVec3 endPt = sweepOrigin + sweepDir*distance;
	PX_ASSERT(!Cm::isEmpty(endPt, sweepExtents));

	const Cm::FastVertex2ShapeScaling meshScaling(triMeshGeom.scale.getInverse()); // shape to vertex transform

	const PxMat33& vertex2ShapeSkew = meshScaling.getVertex2ShapeSkew();

	const PxVec3 originBoundsCenter = vertex2ShapeSkew * sweepOrigin;
	const PxVec3 originBoundsExtents = Cm::basisExtent(vertex2ShapeSkew.column0, vertex2ShapeSkew.column1, vertex2ShapeSkew.column2, sweepExtents);

	sweepOrigin = originBoundsCenter;
	sweepExtents = originBoundsExtents;
	sweepDir = (vertex2ShapeSkew * endPt) - originBoundsCenter;
	return sweepDir.normalizeSafe();
}

PX_FORCE_INLINE const Gu::TriangleMesh* _getMeshData(const PxTriangleMeshGeometry& meshGeom)
{
	return static_cast<const Gu::TriangleMesh*>(meshGeom.triangleMesh);
}

}

#endif
