// 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_QUERY_SYSTEM_H
#define GU_QUERY_SYSTEM_H

#include "foundation/PxUserAllocated.h"
#include "foundation/PxBitMap.h"
#include "foundation/PxArray.h"
#include "foundation/PxMutex.h"
#include "foundation/PxBounds3.h"
#include "GuPruner.h"
#include "GuActorShapeMap.h"

namespace physx
{
	class PxGeometry;

namespace Gu
{
	class BVH;

	class Adapter
	{
		public:
									Adapter()	{}
		virtual						~Adapter()	{}

		virtual	const PxGeometry&	getGeometry(const PrunerPayload& payload)	const	= 0;
	};

	class PrunerFilter
	{
		public:
									PrunerFilter()	{}
		virtual						~PrunerFilter()	{}

		virtual	bool				processPruner(PxU32 prunerIndex/*, const PxQueryThreadContext* context*/)	const	= 0;
	};

	typedef PxU32	PrunerInfo;
	PX_FORCE_INLINE PrunerInfo		createPrunerInfo(PxU32 prunerIndex, bool isDynamic)		{ return (prunerIndex << 1) | PxU32(isDynamic);	}
	PX_FORCE_INLINE PxU32			getPrunerIndex(PrunerInfo info)							{ return PxU32(info)>>1;						}
	PX_FORCE_INLINE PxU32			getDynamic(PrunerInfo info)								{ return PxU32(info) & 1;						}

	PX_FORCE_INLINE ActorShapeData	createActorShapeData(PrunerInfo info, PrunerHandle h)	{ return (ActorShapeData(h) << 32) | ActorShapeData(info);	}
	PX_FORCE_INLINE PrunerInfo		getPrunerInfo(ActorShapeData data)						{ return PrunerInfo(data);									}
	PX_FORCE_INLINE PrunerHandle	getPrunerHandle(ActorShapeData data)					{ return PrunerHandle(data >> 32);							}

	#define INVALID_ACTOR_SHAPE_DATA	PxU64(-1)

	class QuerySystem : public PxUserAllocated
	{
		public:	// PT: TODO: public only to implement checkPrunerIndex easily, revisit this
		struct PrunerExt : public PxUserAllocated
		{
									PrunerExt(Pruner* pruner, PxU32 preallocated);
									~PrunerExt();

			void					flushMemory();

			void					addToDirtyList(PrunerHandle handle, PxU32 dynamic, const PxTransform& transform, const PxBounds3* userBounds=NULL);
			void					removeFromDirtyList(PrunerHandle handle);
			bool					processDirtyList(const Adapter& adapter, float inflation);

			Pruner*					mPruner;
			PxBitMap				mDirtyMap;
			PxArray<PrunerHandle>	mDirtyList;
			PxU32					mNbStatic;		// nb static objects in pruner
			PxU32					mNbDynamic;		// nb dynamic objects in pruner
			bool					mDirtyStatic;	// true if dirty list contains a static

			struct Data
			{
				PxTransform	mPose;
				PxBounds3	mBounds;
			};
			PxArray<Data>			mDirtyData;

			PX_NOCOPY(PrunerExt)
		};

	public:
		PX_PHYSX_COMMON_API							QuerySystem(PxU64 contextID, float inflation, const Adapter& adapter, bool usesTreeOfPruners=false);
		PX_PHYSX_COMMON_API							~QuerySystem();

		PX_FORCE_INLINE		PxU64					getContextId()						const	{ return mContextID;		}
		PX_FORCE_INLINE		const Adapter&			getAdapter()						const	{ return mAdapter;			}
		PX_FORCE_INLINE		PxU32					getStaticTimestamp()				const	{ return mStaticTimestamp;	}

		PX_PHYSX_COMMON_API	PxU32					addPruner(Pruner* pruner, PxU32 preallocated);
		PX_PHYSX_COMMON_API	void					removePruner(PxU32 prunerIndex);
		PX_FORCE_INLINE		PxU32					getNbPruners()						const	{ return mPrunerExt.size();				}
		PX_FORCE_INLINE		const Pruner*			getPruner(PxU32 index)				const	{ return mPrunerExt[index]->mPruner;	}
		PX_FORCE_INLINE		Pruner*					getPruner(PxU32 index)						{ return mPrunerExt[index]->mPruner;	}

		PX_PHYSX_COMMON_API	ActorShapeData			addPrunerShape(const PrunerPayload& payload, PxU32 prunerIndex, bool dynamic, const PxTransform& transform, const PxBounds3* userBounds=NULL);
		PX_PHYSX_COMMON_API	void					removePrunerShape(ActorShapeData data, PrunerPayloadRemovalCallback* removalCallback);
		PX_PHYSX_COMMON_API	void					updatePrunerShape(ActorShapeData data, bool immediately, const PxTransform& transform, const PxBounds3* userBounds=NULL);

		PX_PHYSX_COMMON_API	const PrunerPayload&	getPayloadData(ActorShapeData data, PrunerPayloadData* ppd=NULL)	const;

		PX_PHYSX_COMMON_API	void					commitUpdates();
		PX_PHYSX_COMMON_API	void					update(bool buildStep, bool commit);
		PX_PHYSX_COMMON_API	void					sync(PxU32 prunerIndex, const PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count);

		PX_PHYSX_COMMON_API	void					flushMemory();

		PX_PHYSX_COMMON_API	void					raycast(const PxVec3& origin, const PxVec3& unitDir, float& inOutDistance, PrunerRaycastCallback& cb, const PrunerFilter* prunerFilter)			const;
		PX_PHYSX_COMMON_API	void					overlap(const ShapeData& queryVolume, PrunerOverlapCallback& cb, const PrunerFilter* prunerFilter)												const;
		PX_PHYSX_COMMON_API	void					sweep(const ShapeData& queryVolume, const PxVec3& unitDir, float& inOutDistance, PrunerRaycastCallback& cb, const PrunerFilter* prunerFilter)	const;

							PxU32					startCustomBuildstep();
							void					customBuildstep(PxU32 index);
							void					finishCustomBuildstep();

							void					createTreeOfPruners();
	private:
							const Adapter&			mAdapter;
							PxArray<PrunerExt*>		mPrunerExt;
							PxArray<PxU32>			mDirtyPruners;
							PxArray<PxU32>			mFreePruners;

							Gu::BVH*				mTreeOfPruners;

							const PxU64				mContextID;
							PxU32					mStaticTimestamp;
							const float				mInflation;	// SQ_PRUNER_EPSILON

							PxMutex					mSQLock;  // to make sure only one query updates the dirty pruner structure if multiple queries run in parallel

							volatile bool			mPrunerNeedsUpdating;
							volatile bool			mTimestampNeedsUpdating;
							const bool				mUsesTreeOfPruners;

							void					processDirtyLists();
		PX_FORCE_INLINE		void					invalidateStaticTimestamp()		{ mStaticTimestamp++;		}

							PX_NOCOPY(QuerySystem)
	};
}
}


#include "geometry/PxGeometryHit.h"
#include "geometry/PxSphereGeometry.h"
#include "geometry/PxCapsuleGeometry.h"
#include "geometry/PxBoxGeometry.h"
#include "geometry/PxConvexMeshGeometry.h"
#include "GuCachedFuncs.h"
#include "GuCapsule.h"
#include "GuBounds.h"

#if PX_VC
#pragma warning(disable: 4355 )	// "this" used in base member initializer list
#endif

namespace physx
{
namespace Gu
{
	// PT: TODO: use templates instead of v-calls?

	// PT: we decouple the filter callback from the rest, so that the same filter callback can easily be reused for all pruner queries.
	// This combines the pre-filter callback and fetching the payload's geometry in a single call. Return null to ignore that object.
	struct PrunerFilterCallback
	{
		virtual	~PrunerFilterCallback()	{}

		// Query's hit flags can be tweaked per object. (Note that 'hitFlags' is unused for overlaps though)
		virtual	const PxGeometry*	validatePayload(const PrunerPayload& payload, PxHitFlags& hitFlags) = 0;
	};

	struct DefaultPrunerRaycastCallback : public PrunerRaycastCallback, public PxRaycastThreadContext
	{
		PxRaycastThreadContext*	mContext;
		PrunerFilterCallback&	mFilterCB;
		const GeomRaycastTable&	mCachedRaycastFuncs;
		const PxVec3&			mOrigin;
		const PxVec3&			mDir;
		PxGeomRaycastHit*		mLocalHits;
		const PxU32				mMaxLocalHits;
		const PxHitFlags		mHitFlags;
		PxGeomRaycastHit		mClosestHit;
		PrunerPayload			mClosestPayload;
		bool					mFoundHit;
		const bool				mAnyHit;

		DefaultPrunerRaycastCallback(PrunerFilterCallback& filterCB, const GeomRaycastTable& funcs, const PxVec3& origin, const PxVec3& dir, float distance, PxU32 maxLocalHits, PxGeomRaycastHit* localHits, PxHitFlags hitFlags, bool anyHit, PxRaycastThreadContext* context=NULL) :
			mContext			(context ? context : this),
			mFilterCB			(filterCB),
			mCachedRaycastFuncs	(funcs),
			mOrigin				(origin),
			mDir				(dir),
			mLocalHits			(localHits),
			mMaxLocalHits		(maxLocalHits),
			mHitFlags			(hitFlags),
			mFoundHit			(false),
			mAnyHit				(anyHit)
		{
			mClosestHit.distance = distance;
		}

		virtual	bool	reportHits(const PrunerPayload& /*payload*/, PxU32 /*nbHits*/, PxGeomRaycastHit* /*hits*/)
		{
			return true;
		}

		virtual bool	invoke(PxReal& aDist, PxU32 primIndex, const PrunerPayload* payloads, const PxTransform* transforms)	PX_OVERRIDE PX_FINAL
		{
			PX_ASSERT(payloads && transforms);

			const PrunerPayload& payload = payloads[primIndex];

			PxHitFlags filteredHitFlags = mHitFlags;
			const PxGeometry* shapeGeom = mFilterCB.validatePayload(payload, filteredHitFlags);
			if(!shapeGeom)
				return true;

			const RaycastFunc func = mCachedRaycastFuncs[shapeGeom->getType()];
			const PxU32 nbHits = func(*shapeGeom, transforms[primIndex], mOrigin, mDir, aDist, filteredHitFlags, mMaxLocalHits, mLocalHits, sizeof(PxGeomRaycastHit), mContext);
			if(!nbHits || !reportHits(payload, nbHits, mLocalHits))
				return true;

			const PxGeomRaycastHit& localHit = mLocalHits[0];
			if(localHit.distance < mClosestHit.distance)
			{
				mFoundHit = true;
				if(mAnyHit)
					return false;

				aDist = localHit.distance;
				mClosestHit = localHit;
				mClosestPayload = payload;
			}
			return true;
		}

		PX_NOCOPY(DefaultPrunerRaycastCallback)
	};

	struct DefaultPrunerRaycastAnyCallback : public DefaultPrunerRaycastCallback
	{
		PxGeomRaycastHit	mLocalHit;

		DefaultPrunerRaycastAnyCallback(PrunerFilterCallback& filterCB, const GeomRaycastTable& funcs, const PxVec3& origin, const PxVec3& dir, float distance) :
			DefaultPrunerRaycastCallback	(filterCB, funcs, origin, dir, distance, 1, &mLocalHit, PxHitFlag::eANY_HIT, true)	{}
	};

	struct DefaultPrunerRaycastClosestCallback : public DefaultPrunerRaycastCallback
	{
		PxGeomRaycastHit	mLocalHit;

		DefaultPrunerRaycastClosestCallback(PrunerFilterCallback& filterCB, const GeomRaycastTable& funcs, const PxVec3& origin, const PxVec3& dir, float distance, PxHitFlags hitFlags) :
			DefaultPrunerRaycastCallback	(filterCB, funcs, origin, dir, distance, 1, &mLocalHit, hitFlags, false)	{}
	};

	struct DefaultPrunerOverlapCallback : public PrunerOverlapCallback, public PxOverlapThreadContext
	{
		PxOverlapThreadContext*	mContext;
		PrunerFilterCallback&	mFilterCB;
		const GeomOverlapTable*	mCachedFuncs;
		const PxGeometry&		mGeometry;
		const PxTransform&		mPose;
		PxHitFlags				mUnused;

		DefaultPrunerOverlapCallback(PrunerFilterCallback& filterCB, const GeomOverlapTable* funcs, const PxGeometry& geometry, const PxTransform& pose, PxOverlapThreadContext* context=NULL) :
			mContext		(context ? context : this),
			mFilterCB		(filterCB),
			mCachedFuncs	(funcs),
			mGeometry		(geometry),
			mPose			(pose)
		{
		}

		virtual	bool	reportHit(const PrunerPayload& /*payload*/)
		{
			return true;
		}

		virtual bool	invoke(PxU32 primIndex, const PrunerPayload* payloads, const PxTransform* transforms)	PX_OVERRIDE PX_FINAL
		{
			PX_ASSERT(payloads && transforms);

			const PrunerPayload& payload = payloads[primIndex];

			const PxGeometry* shapeGeom = mFilterCB.validatePayload(payload, mUnused);
			if(!shapeGeom || !Gu::overlap(mGeometry, mPose, *shapeGeom, transforms[primIndex], mCachedFuncs, mContext))
				return true;

			return reportHit(payload);
		}

		PX_NOCOPY(DefaultPrunerOverlapCallback)
	};

	struct BoxShapeCast
	{
		static PX_FORCE_INLINE PxU32 sweep(	const GeomSweepFuncs& sf, const PxGeometry& geom, const PxTransform& pose,
											const PxGeometry& queryGeom, const PxTransform& queryPose, const ShapeData& queryVolume,
											const PxVec3& unitDir, PxReal distance, PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation, PxSweepThreadContext* context)
		{
			PX_ASSERT(queryGeom.getType()==PxGeometryType::eBOX);
			const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP;
			const SweepBoxFunc func = precise ? sf.preciseBoxMap[geom.getType()] : sf.boxMap[geom.getType()];
			return PxU32(func(geom, pose, static_cast<const PxBoxGeometry&>(queryGeom), queryPose, queryVolume.getGuBox(), unitDir, distance, sweepHit, hitFlags, inflation, context));
		}
	};

	struct SphereShapeCast
	{
		static PX_FORCE_INLINE PxU32 sweep(	const GeomSweepFuncs& sf, const PxGeometry& geom, const PxTransform& pose,
											const PxGeometry& queryGeom, const PxTransform& queryPose, const ShapeData& /*queryVolume*/,
											const PxVec3& unitDir, PxReal distance, PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation, PxSweepThreadContext* context)
		{
			PX_ASSERT(queryGeom.getType()==PxGeometryType::eSPHERE);
			// PT: we don't use sd.getGuSphere() here because PhysX doesn't expose a set of 'SweepSphereFunc' functions,
			// we have to go through a capsule (which is then seen as a sphere internally when the half-length is zero).
			const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(queryGeom);
			const PxCapsuleGeometry capsuleGeom(sphereGeom.radius, 0.0f);
			const Capsule worldCapsule(queryPose.p, queryPose.p, sphereGeom.radius); // AP: precompute?
			const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP;
			const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom.getType()] : sf.capsuleMap[geom.getType()];
			return PxU32(func(geom, pose, capsuleGeom, queryPose, worldCapsule, unitDir, distance, sweepHit, hitFlags, inflation, context));
		}
	};

	struct CapsuleShapeCast
	{
		static PX_FORCE_INLINE PxU32 sweep(	const GeomSweepFuncs& sf, const PxGeometry& geom, const PxTransform& pose,
											const PxGeometry& queryGeom, const PxTransform& queryPose, const ShapeData& queryVolume,
											const PxVec3& unitDir, PxReal distance, PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation, PxSweepThreadContext* context)
		{
			PX_ASSERT(queryGeom.getType()==PxGeometryType::eCAPSULE);
			const bool precise = hitFlags & PxHitFlag::ePRECISE_SWEEP;
			const SweepCapsuleFunc func = precise ? sf.preciseCapsuleMap[geom.getType()] : sf.capsuleMap[geom.getType()];
			return PxU32(func(geom, pose, static_cast<const PxCapsuleGeometry&>(queryGeom), queryPose, queryVolume.getGuCapsule(), unitDir, distance, sweepHit, hitFlags, inflation, context));
		}
	};

	struct ConvexShapeCast
	{
		static PX_FORCE_INLINE PxU32 sweep(	const GeomSweepFuncs& sf, const PxGeometry& geom, const PxTransform& pose,
											const PxGeometry& queryGeom, const PxTransform& queryPose, const ShapeData& /*queryVolume*/,
											const PxVec3& unitDir, PxReal distance, PxGeomSweepHit& sweepHit, PxHitFlags hitFlags, PxReal inflation, PxSweepThreadContext* context)
		{
			PX_ASSERT(queryGeom.getType()==PxGeometryType::eCONVEXMESH);
			const SweepConvexFunc func = sf.convexMap[geom.getType()];
			return PxU32(func(geom, pose, static_cast<const PxConvexMeshGeometry&>(queryGeom), queryPose, unitDir, distance, sweepHit, hitFlags, inflation, context));
		}
	};

	struct DefaultPrunerSweepCallback : public PrunerRaycastCallback, public PxSweepThreadContext
	{
		virtual	bool	reportHit(const PrunerPayload& /*payload*/, PxGeomSweepHit& /*hit*/)
		{
			return true;
		}
	};

	template<class ShapeCast>
	struct DefaultPrunerSweepCallbackT : public DefaultPrunerSweepCallback
	{
		PxSweepThreadContext*	mContext;
		PrunerFilterCallback&	mFilterCB;
		const GeomSweepFuncs&	mCachedFuncs;
		const PxGeometry&		mGeometry;
		const PxTransform&		mPose;
		const ShapeData&		mQueryVolume;
		const PxVec3&			mDir;
		PxGeomSweepHit			mLocalHit;
		const PxHitFlags		mHitFlags;
		PxGeomSweepHit			mClosestHit;
		PrunerPayload			mClosestPayload;
		bool					mFoundHit;
		const bool				mAnyHit;

		DefaultPrunerSweepCallbackT(PrunerFilterCallback& filterCB, const GeomSweepFuncs& funcs,
									const PxGeometry& geometry, const PxTransform& pose, const ShapeData& queryVolume,
									const PxVec3& dir, float distance, PxHitFlags hitFlags, bool anyHit, PxSweepThreadContext* context=NULL) :
			mContext		(context ? context : this),
			mFilterCB		(filterCB),
			mCachedFuncs	(funcs),
			mGeometry		(geometry),
			mPose			(pose),
			mQueryVolume	(queryVolume),
			mDir			(dir),
			mHitFlags		(hitFlags),
			mFoundHit		(false),
			mAnyHit			(anyHit)
		{
			mClosestHit.distance = distance;
		}

		virtual bool	invoke(PxReal& aDist, PxU32 primIndex, const PrunerPayload* payloads, const PxTransform* transforms)	PX_OVERRIDE PX_FINAL
		{
			PX_ASSERT(payloads && transforms);

			const PrunerPayload& payload = payloads[primIndex];

			PxHitFlags filteredHitFlags = mHitFlags;
			const PxGeometry* shapeGeom = mFilterCB.validatePayload(payload, filteredHitFlags);
			if(!shapeGeom)
				return true;

			// PT: ### TODO: missing bit from PhysX version here

			const float inflation = 0.0f;	// ####
			const PxU32 retVal = ShapeCast::sweep(mCachedFuncs, *shapeGeom, transforms[primIndex], mGeometry, mPose, mQueryVolume, mDir, aDist, mLocalHit, filteredHitFlags, inflation, mContext);

			if(!retVal || !reportHit(payload, mLocalHit))
				return true;

			if(mLocalHit.distance < mClosestHit.distance)
			{
				mFoundHit = true;
				if(mAnyHit)
					return false;

				aDist = mLocalHit.distance;
				mClosestHit = mLocalHit;
				mClosestPayload = payload;
			}
			return true;
		}

		PX_NOCOPY(DefaultPrunerSweepCallbackT)
	};

typedef DefaultPrunerSweepCallbackT<SphereShapeCast>	DefaultPrunerSphereSweepCallback;
typedef DefaultPrunerSweepCallbackT<BoxShapeCast>		DefaultPrunerBoxSweepCallback;
typedef DefaultPrunerSweepCallbackT<CapsuleShapeCast>	DefaultPrunerCapsuleSweepCallback;
typedef DefaultPrunerSweepCallbackT<ConvexShapeCast>	DefaultPrunerConvexSweepCallback;

}
}

#endif
