// 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.  

// ****************************************************************************
// This snippet illustrates how to use a custom query system and the low-level
// cooking functions.
//
// This is similar to SnippetStandaloneBVH, but this time using a more
// advanced query system instead of a single PxBVH.
//
// This snippet illustrates a basic setup and a single type of query
// (raycast closest hit). For more queries see SnippetQuerySystemAllQueries.
//
// ****************************************************************************

#include <ctype.h>
#include "PxPhysicsAPI.h"
#include "GuQuerySystem.h"
#include "GuFactory.h"
#include "GuCooking.h"
#include "foundation/PxArray.h"
#include "../snippetcommon/SnippetPrint.h"
#include "../snippetcommon/SnippetPVD.h"
#include "../snippetutils/SnippetUtils.h"
#ifdef RENDER_SNIPPET
	#include "../snippetrender/SnippetCamera.h"
	#include "../snippetrender/SnippetRender.h"
#endif

using namespace physx;
using namespace Gu;

// The query system can compute the bounds for you, or you can do it manually. Manual bounds
// computation can be useful for specific effects (like using temporal bounds) or if there is
// an external system that already computed the bounds and recomputing them would be a waste.
// Automatic bounds computation is easier to use and less error-prone.
static const bool gManualBoundsComputation = false;

// The query system can delay internal transform/bounds updates or use the data immediately.
// Delaying updates can be faster due to batching, but it uses more memory to store the data until
// the actual update happens. Delaying the update can also serve as a double-buffering mechanism,
// allowing one to query the old state of the system until a later user-controlled point in time.
static const bool gUseDelayedUpdates = true;

// Bounds in the query system can be inflated a bit to fight numerical inaccuracy errors that can happen
// when a ray or a query-volume just touches the bounds. Users can manually inflate bounds or let the
// system do it. Because the system can compute the bounds automatically, it is necessary to let it know
// about the inflation value.
static const float gBoundsInflation = 0.001f;

#define MAX_NB_OBJECTS	32

namespace
{
	class CustomScene : public Adapter
	{
		public:
			CustomScene();
			~CustomScene()	{}

			// Adapter
			virtual	const PxGeometry&	getGeometry(const PrunerPayload& payload)	const;
			//~Adapter

			void	release();
			void	addGeom(const PxGeometry& geom, const PxTransform& pose);
			void	render();
			bool	raycast(const PxVec3& origin, const PxVec3& unitDir, float maxDist, PxGeomRaycastHit& hit)	const;
			void	updateObjects();

			struct Object
			{
				PxGeometryHolder	mGeom;
				ActorShapeData		mData;
			};

			PxU32			mNbObjects;
			Object			mObjects[MAX_NB_OBJECTS];
			QuerySystem*	mQuerySystem;
			PxU32			mPrunerIndex;
	};

static const PxGeometry& getGeometryFromPayload(const PrunerPayload& payload)
{
	const CustomScene* cs = reinterpret_cast<const CustomScene*>(payload.data[1]);
	return cs->mObjects[PxU32(payload.data[0])].mGeom.any();
}

const PxGeometry& CustomScene::getGeometry(const PrunerPayload& payload) const
{
	// This function is called by the system to compute bounds. It will never be
	// called if 'gManualBoundsComputation' is true.
	PX_ASSERT(!gManualBoundsComputation);

	return getGeometryFromPayload(payload);
}

void CustomScene::release()
{
	PX_DELETE(mQuerySystem);
	PX_DELETE_THIS;
}

CustomScene::CustomScene() : mNbObjects(0)
{
	// The contextID is a parameter sent to the profiler to identify the owner of a profile event.
	// In PhysX this is usually the PxScene pointer, and it is used by PVD to group together all profile events of a given scene.
	// We do not have a PxScene object here so we can put an arbitrary value there. The value will only be used by
	// PVD to display profiling results.
	const PxU64 contextID = PxU64(this);

	// First we create a query system and give it an adapter. The adapter is used to retrieve the geometry
	// of objects in the system. This is needed when the system automatically computes bounds for users.
	// The geometry is not stored directly in the system to avoid duplication, and make it easy to reuse
	// the system with PxShape-based objects. In this snippet the system will call the
	// 'CustomScene::getGeometry' function above to fetch an object's geometry.
	mQuerySystem = PX_NEW(QuerySystem)(contextID, gBoundsInflation, *this);

	// PhysX uses a hardcoded number of pruners (one for static objects, one for dynamic objects,
	// and an optional one for compound). The query system here is more flexible and supports an
	// arbitrary number of pruners, which have to be created by users and added to the system
	// explicitly. In this snippet we just use a single pruner of a chosen type:
	Pruner* pruner = createAABBPruner(contextID, true, COMPANION_PRUNER_INCREMENTAL, BVH_SPLATTER_POINTS, 4);

	// Then we add it to the query system, which takes ownership of the object (it will delete
	// the pruner when the query system is released). Each pruner is given an index by the
	// system, used in 'addPrunerShape' to identify which pruner each object is added to.
	mPrunerIndex = mQuerySystem->addPruner(pruner, 0);
}

void CustomScene::addGeom(const PxGeometry& geom, const PxTransform& pose)
{
	PX_ASSERT(mQuerySystem);

	// The query system operates on anonymous 'payloads', which are basically glorified user-data.
	// We put here what we need to implement 'CustomScene::getGeometry'.
	PrunerPayload payload;
	payload.data[0] = mNbObjects;	// This will be the index of our new object, see below.
	payload.data[1] = size_t(this);

	// We store the geometry first, because in automatic mode the 'CustomScene::getGeometry' function
	// will be called by 'addPrunerShape' below, so we need the geometry to be properly setup first.
	Object& obj = mObjects[mNbObjects];
	obj.mGeom.storeAny(geom);

	// The query system manages a built-in timestamp for static objects, which is used by external
	// sub-systems like character controllers to invalidate their caches. This is not needed in
	// this snippet so any value works here.
	const bool isDynamic = true;

	// In automatic mode the system will compute the bounds for us.
	// In manual mode we compute the bounds first and pass them to the system.
	//
	// Note that contrary to bounds, the transforms are duplicated and stored within the query
	// system. In this snippet we take advantage of this by not storing the poses anywhere
	// else - we will retrieve them from the query system when we need them. In a more complex
	// example this could create a duplication of the transforms, but this 'double-buffering' is
	// actually done on purpose to make sure the query system can run in parallel to the app's
	// code when/if it modifies the objects' poses. The poses are double-buffered but the geometries
	// are not, because poses of dynamic objects change each frame while geometries do not.
	if(gManualBoundsComputation)
	{
		PxBounds3 bounds;
		PxGeometryQuery::computeGeomBounds(bounds, geom, pose, 0.0f, 1.0f + gBoundsInflation);
		obj.mData = mQuerySystem->addPrunerShape(payload, mPrunerIndex, isDynamic, pose, &bounds);
	}
	else
	{
		obj.mData = mQuerySystem->addPrunerShape(payload, mPrunerIndex, isDynamic, pose, NULL);
	}

	mNbObjects++;
}

void CustomScene::updateObjects()
{
	if(!mQuerySystem)
		return;

	static float time = 0.0f;
	time += 0.01f;

	const PxU32 nbObjects = mNbObjects;
	for(PxU32 i=0;i<nbObjects;i++)
	{
//		const float coeff = float(i)/float(nbObjects);
		const float coeff = float(i);

		// Compute an arbitrary new pose for this object
		PxTransform pose;
		{
			pose.p.x = sinf(time)*cosf(time+coeff)*10.0f;
			pose.p.y = sinf(time*1.17f)*cosf(time*1.17f+coeff)*2.0f;
			pose.p.z = sinf(time*0.33f)*cosf(time*0.33f+coeff)*10.0f;

			PxMat33 rotX;	PxSetRotX(rotX, time+coeff);
			PxMat33 rotY;	PxSetRotY(rotY, time*1.17f+coeff);
			PxMat33 rotZ;	PxSetRotZ(rotZ, time*0.33f+coeff);
			PxMat33 rot = rotX * rotY * rotZ;
			pose.q = PxQuat(rot);
			pose.q.normalize();
		}

		// Now we're going to tell the query system about it. It is important to
		// understand that updating the query system is a multiple-steps process:
		// a) we need to store the new transform and/or bounds in the system.
		// b) the internal data-structures have to be updated to take a) into account.
		//
		// For example if the internal data-structure is an AABB-tree (but it doesn't
		// have to be, this is pruner-dependent) then (a) would be writing the new bounds
		// value in a leaf node, while (b) would be refitting the tree accordingly. Or
		// in a different implementation (a) could be storing the bounds in a linear
		// array and (b) could be rebuilding the tree from scratch.
		//
		// The important point is that there is a per-object update (a), and a global
		// per-pruner update (b). It would be very inefficient to do (a) and (b)
		// sequentially for each object (we don't want to rebuild the tree more than
		// once for example) so the update process is separated into two clearly
		// distinct phases. The phase we're dealing with here is (a), via the
		// 'updatePrunerShape' function.

		const Object& obj = mObjects[i];

		if(gManualBoundsComputation)
		{
			PxBounds3 bounds;
			PxGeometryQuery::computeGeomBounds(bounds, obj.mGeom.any(), pose, 0.0f, 1.0f + gBoundsInflation);
			mQuerySystem->updatePrunerShape(obj.mData, !gUseDelayedUpdates, pose, &bounds);
		}
		else
		{
			// Note: in this codepath the system will compute the bounds automatically:
			// - if 'immediately' is true, the system will call back 'CustomScene::getGeometry'
			//   during the 'updatePrunerShape' call.
			// - otherwise it will call 'CustomScene::getGeometry' later during the
			//   'mQuerySystem->update' call (below).
			mQuerySystem->updatePrunerShape(obj.mData, !gUseDelayedUpdates, pose, NULL);
		}
	}

	// This is the per-pruner update (b) we mentioned just above. It commits the
	// updates we just made and reflects them into the internal data-structures.
	//
	// Note that this function must also be called after adding & removing objects.
	//
	// Finally, this function also manages the incremental rebuild of internal structures
	// if the 'buildStep' parameter is true. This is a convenience function that does
	// everything needed in a single call. If all the updates to the system happen in
	//  a single place, like in this snippet, then this is everything you need.
	mQuerySystem->update(true, true);
}

namespace
{
	struct CustomPrunerFilterCallback : public PrunerFilterCallback
	{
		virtual	const PxGeometry*	validatePayload(const PrunerPayload& payload, PxHitFlags& /*hitFlags*/)
		{
			return &getGeometryFromPayload(payload);
		}
	};
}

static CustomPrunerFilterCallback	gFilterCallback;
static CachedFuncs					gCachedFuncs;

bool CustomScene::raycast(const PxVec3& origin, const PxVec3& unitDir, float maxDist, PxGeomRaycastHit& hit) const
{
	if(!mQuerySystem)
		return false;

	// In this snippet our update loop is simple:
	//
	// - update all objects ('mQuerySystem->updatePrunerShape')
	// - call 'mQuerySystem->update'
	// - then perform raycasts
	//
	// Because we call 'mQuerySystem->update' just before performing the raycasts,
	// we guarantee that the internal data-structures are up-to-date and we can
	// immediately call 'mQuerySystem->raycast'.
	//
	// However things can become more complicated:
	// - sometimes 'mQuerySystem->updatePrunerShape' is immediately followed by a
	//   raycast (ex: spawning an object, using a raycast to locate it on the map, repeat)
	// - sometimes raycasts happen from multiple threads in no clear order
	//
	// In these cases the following call to 'commitUpdates' is needed to commit the
	// minimal amount of updates to the system, so that correct raycast results are
	// guaranteed. In particular this call omits the 'build step' performed in the
	// main 'mQuerySystem->update' function (users should have one build step per
	// frame). This function is also thread-safe, i.e. you can call commitUpdates
	// and raycasts from multiple threads (contrary to 'mQuerySystem->update').
	//
	// Note that in PxScene::raycast() this call is always executed. But this custom
	// query system lets users control when & where it happens. The system becomes
	// more flexible, but puts more burden on users.

	if(0)
		mQuerySystem->commitUpdates();

	// After that the raycast code itself is rather simple.

	DefaultPrunerRaycastClosestCallback CB(gFilterCallback, gCachedFuncs.mCachedRaycastFuncs, origin, unitDir, maxDist, PxHitFlag::eDEFAULT);

	mQuerySystem->raycast(origin, unitDir, maxDist, CB, NULL);

	if(CB.mFoundHit)
		hit = CB.mClosestHit;

	return CB.mFoundHit;
}

void CustomScene::render()
{
	updateObjects();

#ifdef RENDER_SNIPPET
	const PxVec3 color(1.0f, 0.5f, 0.25f);

	const PxU32 nbObjects = mNbObjects;
	for(PxU32 i=0;i<nbObjects;i++)
	{
		const Object& obj = mObjects[i];

		PrunerPayloadData ppd;
		mQuerySystem->getPayloadData(obj.mData, &ppd);

		Snippets::DrawBounds(*ppd.mBounds);

		Snippets::renderGeoms(1, &obj.mGeom, ppd.mTransform, false, color);
	}

	//mQuerySystem->visualize(true, true, PxRenderOutput)

	const PxU32 screenWidth = Snippets::getScreenWidth();
	const PxU32 screenHeight = Snippets::getScreenHeight();
	Snippets::Camera* sCamera = Snippets::getCamera();
	const PxVec3 camPos = sCamera->getEye();
	const PxVec3 camDir = sCamera->getDir();
#if PX_DEBUG
	const PxU32	RAYTRACING_RENDER_WIDTH = 64;
	const PxU32	RAYTRACING_RENDER_HEIGHT = 64;
#else
	const PxU32	RAYTRACING_RENDER_WIDTH = 256;
	const PxU32	RAYTRACING_RENDER_HEIGHT = 256;
#endif

	const PxU32 textureWidth = RAYTRACING_RENDER_WIDTH;
	const PxU32 textureHeight = RAYTRACING_RENDER_HEIGHT;

	GLubyte* pixels = new GLubyte[textureWidth*textureHeight*4];

	const float fScreenWidth = float(screenWidth)/float(RAYTRACING_RENDER_WIDTH);
	const float fScreenHeight = float(screenHeight)/float(RAYTRACING_RENDER_HEIGHT);

	{
		// Contrary to PxBVH, the Gu-level query system does not contain any built-in "SIMD guard".
		// It is up to users to make sure the SIMD control word is properly setup before calling
		// these low-level functions. See also OPTIM_SKIP_INTERNAL_SIMD_GUARD in SnippetPathTracing.
		PX_SIMD_GUARD

		GLubyte* buffer = pixels;
		for(PxU32 j=0;j<RAYTRACING_RENDER_HEIGHT;j++)
		{
			const PxU32 yi = PxU32(fScreenHeight*float(j));
			for(PxU32 i=0;i<RAYTRACING_RENDER_WIDTH;i++)
			{
				const PxU32 xi = PxU32(fScreenWidth*float(i));
				const PxVec3 dir = Snippets::computeWorldRay(xi, yi, camDir);

				PxGeomRaycastHit hit;
				if(raycast(camPos, dir, 5000.0f, hit))
				{
					buffer[0] = 128+GLubyte(hit.normal.x*127.0f);
					buffer[1] = 128+GLubyte(hit.normal.y*127.0f);
					buffer[2] = 128+GLubyte(hit.normal.z*127.0f);
					buffer[3] = 255;
				}
				else
				{
					buffer[0] = 0;
					buffer[1] = 0;
					buffer[2] = 0;
					buffer[3] = 255;
				}

				buffer+=4;
			}
		}
	}

	const GLuint texID = Snippets::CreateTexture(textureWidth, textureHeight, pixels, false);

#if PX_DEBUG
	Snippets::DisplayTexture(texID, 256, 10);
#else
	Snippets::DisplayTexture(texID, RAYTRACING_RENDER_WIDTH, 10);
#endif

	delete [] pixels;
	Snippets::ReleaseTexture(texID);
#endif
}

}

static CustomScene* gScene = NULL;

static PxDefaultAllocator		gAllocator;
static PxDefaultErrorCallback	gErrorCallback;
static PxFoundation*			gFoundation = NULL;
static PxConvexMesh*			gConvexMesh = NULL;
static PxTriangleMesh*			gTriangleMesh = NULL;

void initPhysics(bool /*interactive*/)
{
	// We first initialize the PhysX libs we need to create PxGeometry-based objects.
	// That is only Foundation, since we'll use the low-level cooking functions here.
	// (We don't need to initialize the cooking library).
	// Also note how we are not going to use a PxScene in this snippet, and in fact
	// we're not going to need anything from the main PhysX_xx.dll, we only use
	// PhysXCommon_xx.dll.
	gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback);

	// Cook one convex mesh and one triangle mesh used in this snippet
	{
		// Some cooking parameters will have an impact on the performance of our queries,
		// some will not. Generally speaking midphase-related parameters are still important
		// here, while anything related to contact-generation can be disabled.
		const PxTolerancesScale scale;
		PxCookingParams params(scale);
		params.midphaseDesc.setToDefault(PxMeshMidPhase::eBVH34);
	//	params.midphaseDesc.mBVH34Desc.quantized = false;
		params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_ACTIVE_EDGES_PRECOMPUTE;
		params.meshPreprocessParams |= PxMeshPreprocessingFlag::eDISABLE_CLEAN_MESH;

		// The convex mesh
		{
			const PxF32 width = 3.0f;
			const PxF32 radius = 1.0f;

			PxVec3 points[2*16];
			for(PxU32 i = 0; i < 16; i++)
			{
				const PxF32 cosTheta = PxCos(i*PxPi*2.0f/16.0f);
				const PxF32 sinTheta = PxSin(i*PxPi*2.0f/16.0f);
				const PxF32 y = radius*cosTheta;
				const PxF32 z = radius*sinTheta;
				points[2*i+0] = PxVec3(-width/2.0f, y, z);
				points[2*i+1] = PxVec3(+width/2.0f, y, z);
			}

			PxConvexMeshDesc convexDesc;
			convexDesc.points.count		= 32;
			convexDesc.points.stride	= sizeof(PxVec3);
			convexDesc.points.data		= points;
			convexDesc.flags			= PxConvexFlag::eCOMPUTE_CONVEX;
			gConvexMesh = immediateCooking::createConvexMesh(params, convexDesc);
		}

		// The triangle mesh
		{
			PxTriangleMeshDesc meshDesc;
			meshDesc.points.count		= SnippetUtils::Bunny_getNbVerts();
			meshDesc.points.stride		= sizeof(PxVec3);
			meshDesc.points.data		= SnippetUtils::Bunny_getVerts();
			meshDesc.triangles.count	= SnippetUtils::Bunny_getNbFaces();
			meshDesc.triangles.stride	= sizeof(int)*3;
			meshDesc.triangles.data		= SnippetUtils::Bunny_getFaces();

			gTriangleMesh = immediateCooking::createTriangleMesh(params, meshDesc);
		}
	}

	// Create our custom scene and populate it with some custom PxGeometry-based objects
	{
		gScene = new CustomScene;
		gScene->addGeom(PxBoxGeometry(PxVec3(1.0f, 2.0f, 0.5f)), PxTransform(PxVec3(0.0f, 0.0f, 0.0f)));
		gScene->addGeom(PxSphereGeometry(1.5f), PxTransform(PxVec3(4.0f, 0.0f, 0.0f)));
		gScene->addGeom(PxCapsuleGeometry(1.0f, 1.0f), PxTransform(PxVec3(-4.0f, 0.0f, 0.0f)));
		gScene->addGeom(PxConvexMeshGeometry(gConvexMesh), PxTransform(PxVec3(0.0f, 0.0f, 4.0f)));
		gScene->addGeom(PxTriangleMeshGeometry(gTriangleMesh), PxTransform(PxVec3(0.0f, 0.0f, -4.0f)));
	}
}

void renderScene()
{
	if(gScene)
		gScene->render();
}

void stepPhysics(bool /*interactive*/)
{
}

void cleanupPhysics(bool /*interactive*/)
{
	PX_RELEASE(gScene);
	PX_RELEASE(gTriangleMesh);
	PX_RELEASE(gConvexMesh);
	PX_RELEASE(gFoundation);

	printf("SnippetStandaloneQuerySystem done.\n");
}

void keyPress(unsigned char /*key*/, const PxTransform& /*camera*/)
{
}

int snippetMain(int, const char*const*)
{
	printf("Standalone Query System snippet.\n");

#ifdef RENDER_SNIPPET
	extern void renderLoop();
	renderLoop();
#else
	static const PxU32 frameCount = 100;
	initPhysics(false);
	for(PxU32 i=0; i<frameCount; i++)
		stepPhysics(false);
	cleanupPhysics(false);
#endif

	return 0;
}




