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

#include "foundation/PxPreprocessor.h"

#define USE_CUDA_INTEROP (PX_SUPPORT_GPU_PHYSX)

#if (PX_SUPPORT_GPU_PHYSX && USE_CUDA_INTEROP)
#if PX_LINUX && PX_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#endif
#include "cuda.h"
#include "SnippetRender.h"
#include "cudaGL.h"
#if PX_LINUX && PX_CLANG
#pragma clang diagnostic pop
#endif
#else
#include "SnippetRender.h"
#endif

#include "SnippetFontRenderer.h"
#include "SnippetCamera.h"
#include "foundation/PxArray.h"
#include "foundation/PxMathUtils.h"
#include <vector>

#define MAX_NUM_ACTOR_SHAPES	128
#define	INITIAL_SCREEN_WIDTH	768
#define	INITIAL_SCREEN_HEIGHT	768

using namespace physx;

static GLFontRenderer	gTexter;

static float gCylinderData[]={
	1.0f,0.0f,1.0f,1.0f,0.0f,1.0f,1.0f,0.0f,0.0f,1.0f,0.0f,0.0f,
	0.866025f,0.500000f,1.0f,0.866025f,0.500000f,1.0f,0.866025f,0.500000f,0.0f,0.866025f,0.500000f,0.0f,
	0.500000f,0.866025f,1.0f,0.500000f,0.866025f,1.0f,0.500000f,0.866025f,0.0f,0.500000f,0.866025f,0.0f,
	-0.0f,1.0f,1.0f,-0.0f,1.0f,1.0f,-0.0f,1.0f,0.0f,-0.0f,1.0f,0.0f,
	-0.500000f,0.866025f,1.0f,-0.500000f,0.866025f,1.0f,-0.500000f,0.866025f,0.0f,-0.500000f,0.866025f,0.0f,
	-0.866025f,0.500000f,1.0f,-0.866025f,0.500000f,1.0f,-0.866025f,0.500000f,0.0f,-0.866025f,0.500000f,0.0f,
	-1.0f,-0.0f,1.0f,-1.0f,-0.0f,1.0f,-1.0f,-0.0f,0.0f,-1.0f,-0.0f,0.0f,
	-0.866025f,-0.500000f,1.0f,-0.866025f,-0.500000f,1.0f,-0.866025f,-0.500000f,0.0f,-0.866025f,-0.500000f,0.0f,
	-0.500000f,-0.866025f,1.0f,-0.500000f,-0.866025f,1.0f,-0.500000f,-0.866025f,0.0f,-0.500000f,-0.866025f,0.0f,
	0.0f,-1.0f,1.0f,0.0f,-1.0f,1.0f,0.0f,-1.0f,0.0f,0.0f,-1.0f,0.0f,
	0.500000f,-0.866025f,1.0f,0.500000f,-0.866025f,1.0f,0.500000f,-0.866025f,0.0f,0.500000f,-0.866025f,0.0f,
	0.866026f,-0.500000f,1.0f,0.866026f,-0.500000f,1.0f,0.866026f,-0.500000f,0.0f,0.866026f,-0.500000f,0.0f,
	1.0f,0.0f,1.0f,1.0f,0.0f,1.0f,1.0f,0.0f,0.0f,1.0f,0.0f,0.0f
};

static std::vector<PxVec3>* gVertexBuffer = NULL;
static int gLastTime = 0;
static int gFrameCounter = 0;
static char gTitle[256];
static bool gWireFrame = false;

static PX_FORCE_INLINE void prepareVertexBuffer()
{
	if(!gVertexBuffer)
		gVertexBuffer = new std::vector<PxVec3>;
	gVertexBuffer->clear();
}

static PX_FORCE_INLINE void pushVertex(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxVec3& n)
{
	PX_ASSERT(gVertexBuffer);
	gVertexBuffer->push_back(n);	gVertexBuffer->push_back(v0);
	gVertexBuffer->push_back(n);	gVertexBuffer->push_back(v1);
	gVertexBuffer->push_back(n);	gVertexBuffer->push_back(v2);
}

static PX_FORCE_INLINE void pushVertex(const PxVec3& v0, const PxVec3& v1, const PxVec3& v2, const PxVec3& n0, const PxVec3& n1, const PxVec3& n2, float normalSign = 1)
{
	PX_ASSERT(gVertexBuffer);
	gVertexBuffer->push_back(normalSign * n0); gVertexBuffer->push_back(v0);
	gVertexBuffer->push_back(normalSign * n1); gVertexBuffer->push_back(v1);
	gVertexBuffer->push_back(normalSign * n2); gVertexBuffer->push_back(v2);
}

static PX_FORCE_INLINE const PxVec3* getVertexBuffer()
{
	PX_ASSERT(gVertexBuffer);
	return &(*gVertexBuffer)[0];
}

static void releaseVertexBuffer()
{
	if(gVertexBuffer)
	{
		delete gVertexBuffer;
		gVertexBuffer = NULL;
	}
}

static void renderDeformableVolumeGeometry(const PxTetrahedronMesh& mesh, const PxVec4* deformedPositionsInvMass)
{
	const int tetFaces[4][3] = { {0,2,1}, {0,1,3}, {0,3,2}, {1,2,3} };

	//Get the deformed vertices			
	//const PxVec3* vertices = mesh.getVertices();
	const PxU32 tetCount = mesh.getNbTetrahedrons();
	const PxU32 has16BitIndices = mesh.getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
	const void* indexBuffer = mesh.getTetrahedrons();

	prepareVertexBuffer();

	const PxU32* intIndices = reinterpret_cast<const PxU32*>(indexBuffer);
	const PxU16* shortIndices = reinterpret_cast<const PxU16*>(indexBuffer);
	PxU32 numTotalTriangles = 0;
	PX_UNUSED(numTotalTriangles);

	for (PxU32 i = 0; i < tetCount; ++i)
	{
		PxU32 vref[4];
		if (has16BitIndices)
		{
			vref[0] = *shortIndices++;
			vref[1] = *shortIndices++;
			vref[2] = *shortIndices++;
			vref[3] = *shortIndices++;
		}
		else
		{
			vref[0] = *intIndices++;
			vref[1] = *intIndices++;
			vref[2] = *intIndices++;
			vref[3] = *intIndices++;
		}

		for (PxU32 j = 0; j < 4; ++j)
		{
			const PxVec4& v0 = deformedPositionsInvMass[vref[tetFaces[j][0]]];
			const PxVec4& v1 = deformedPositionsInvMass[vref[tetFaces[j][1]]];
			const PxVec4& v2 = deformedPositionsInvMass[vref[tetFaces[j][2]]];

			PxVec3 fnormal = (v1.getXYZ() - v0.getXYZ()).cross(v2.getXYZ() - v0.getXYZ());
			fnormal.normalize();

			pushVertex(v0.getXYZ(), v1.getXYZ(), v2.getXYZ(), fnormal);
			numTotalTriangles++;
		}
	}
	glPushMatrix();
	glScalef(1.0f, 1.0f, 1.0f);
	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	const PxVec3* vertexBuffer = getVertexBuffer();
	glNormalPointer(GL_FLOAT, 2 * 3 * sizeof(float), vertexBuffer);
	glVertexPointer(3, GL_FLOAT, 2 * 3 * sizeof(float), vertexBuffer + 1);
	glDrawArrays(GL_TRIANGLES, 0, int(numTotalTriangles * 3));
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glPopMatrix();
}

static void renderGeometry(const PxGeometry& geom)
{
	if (gWireFrame)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	switch(geom.getType())
	{
		case PxGeometryType::eBOX:
		{
			const PxBoxGeometry& boxGeom = static_cast<const PxBoxGeometry&>(geom);
			glScalef(boxGeom.halfExtents.x, boxGeom.halfExtents.y, boxGeom.halfExtents.z);
			glutSolidCube(2);
		}
		break;

		case PxGeometryType::eSPHERE:
		{
			const PxSphereGeometry& sphereGeom = static_cast<const PxSphereGeometry&>(geom);
			glutSolidSphere(GLdouble(sphereGeom.radius), 10, 10);
		}
		break;

		case PxGeometryType::eCAPSULE:
		{
			const PxCapsuleGeometry& capsuleGeom = static_cast<const PxCapsuleGeometry&>(geom);
			const PxF32 radius = capsuleGeom.radius;
			const PxF32 halfHeight = capsuleGeom.halfHeight;

			//Sphere
			glPushMatrix();
			glTranslatef(halfHeight, 0.0f, 0.0f);
			glScalef(radius,radius,radius);
			glutSolidSphere(1, 10, 10);		
			glPopMatrix();

			//Sphere
			glPushMatrix();
			glTranslatef(-halfHeight, 0.0f, 0.0f);
			glScalef(radius,radius,radius);
			glutSolidSphere(1, 10, 10);		
			glPopMatrix();

			//Cylinder
			glPushMatrix();
			glTranslatef(-halfHeight, 0.0f, 0.0f);
			glScalef(2.0f*halfHeight, radius,radius);
			glRotatef(90.0f,0.0f,1.0f,0.0f);
			glEnableClientState(GL_VERTEX_ARRAY);
			glEnableClientState(GL_NORMAL_ARRAY);
			glVertexPointer(3, GL_FLOAT, 2*3*sizeof(float), gCylinderData);
			glNormalPointer(GL_FLOAT, 2*3*sizeof(float), gCylinderData+3);
			glDrawArrays(GL_TRIANGLE_STRIP, 0, 13*2);
			glDisableClientState(GL_VERTEX_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glPopMatrix();
		}
		break;

		case PxGeometryType::eCONVEXMESH:
		{
			const PxConvexMeshGeometry& convexGeom = static_cast<const PxConvexMeshGeometry&>(geom);

			//Compute triangles for each polygon.
			const PxVec3& scale = convexGeom.scale.scale;
			PxConvexMesh* mesh = convexGeom.convexMesh;
			const PxU32 nbPolys = mesh->getNbPolygons();
			const PxU8* polygons = mesh->getIndexBuffer();
			const PxVec3* verts = mesh->getVertices();
			PxU32 nbVerts = mesh->getNbVertices();
			PX_UNUSED(nbVerts);

			prepareVertexBuffer();

			PxU32 numTotalTriangles = 0;
			for(PxU32 i = 0; i < nbPolys; i++)
			{
				PxHullPolygon data;
				mesh->getPolygonData(i, data);

				const PxU32 nbTris = PxU32(data.mNbVerts - 2);
				const PxU8 vref0 = polygons[data.mIndexBase + 0];
				PX_ASSERT(vref0 < nbVerts);
				for(PxU32 j=0;j<nbTris;j++)
				{
					const PxU32 vref1 = polygons[data.mIndexBase + 0 + j + 1];
					const PxU32 vref2 = polygons[data.mIndexBase + 0 + j + 2];

					//generate face normal:
					PxVec3 e0 = verts[vref1] - verts[vref0];
					PxVec3 e1 = verts[vref2] - verts[vref0];

					PX_ASSERT(vref1 < nbVerts);
					PX_ASSERT(vref2 < nbVerts);

					PxVec3 fnormal = e0.cross(e1);
					fnormal.normalize();

					pushVertex(verts[vref0], verts[vref1], verts[vref2], fnormal);
					numTotalTriangles++;
				}
			}
			glPushMatrix();
			glScalef(scale.x, scale.y, scale.z);
			glEnableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			const PxVec3* vertexBuffer = getVertexBuffer();
			glNormalPointer(GL_FLOAT, 2*3*sizeof(float), vertexBuffer);
			glVertexPointer(3, GL_FLOAT, 2*3*sizeof(float), vertexBuffer+1);
			glDrawArrays(GL_TRIANGLES, 0, int(numTotalTriangles * 3));
			glPopMatrix();
		}
		break;

		case PxGeometryType::eTRIANGLEMESH:
		{
			const PxTriangleMeshGeometry& triGeom = static_cast<const PxTriangleMeshGeometry&>(geom);

			const PxTriangleMesh& mesh = *triGeom.triangleMesh;
			const PxVec3 scale = triGeom.scale.scale;

			const PxU32 triangleCount = mesh.getNbTriangles();
			const PxU32 has16BitIndices = mesh.getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES;
			const void* indexBuffer = mesh.getTriangles();

			const PxVec3* vertices = mesh.getVertices();

			prepareVertexBuffer();

			const PxU32* intIndices = reinterpret_cast<const PxU32*>(indexBuffer);
			const PxU16* shortIndices = reinterpret_cast<const PxU16*>(indexBuffer);
			PxU32 numTotalTriangles = 0;
			for(PxU32 i=0; i < triangleCount; ++i)
			{
				PxU32 vref0, vref1, vref2;
				if(has16BitIndices)
				{
					vref0 = *shortIndices++;
					vref1 = *shortIndices++;
					vref2 = *shortIndices++;
				}
				else
				{
					vref0 = *intIndices++;
					vref1 = *intIndices++;
					vref2 = *intIndices++;
				}

				const PxVec3& v0 = vertices[vref0];
				const PxVec3& v1 = vertices[vref1];
				const PxVec3& v2 = vertices[vref2];

				PxVec3 fnormal = (v1 - v0).cross(v2 - v0);
				fnormal.normalize();

				pushVertex(v0, v1, v2, fnormal);
				numTotalTriangles++;
			}
			glPushMatrix();
			glScalef(scale.x, scale.y, scale.z);
			glEnableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			const PxVec3* vertexBuffer = getVertexBuffer();
			glNormalPointer(GL_FLOAT, 2*3*sizeof(float), vertexBuffer);
			glVertexPointer(3, GL_FLOAT, 2*3*sizeof(float), vertexBuffer+1);
			glDrawArrays(GL_TRIANGLES, 0, int(numTotalTriangles * 3));
			glDisableClientState(GL_VERTEX_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glPopMatrix();
		}
		break;

		case PxGeometryType::eTETRAHEDRONMESH: 
		{
			const int tetFaces[4][3] = { {0,2,1}, {0,1,3}, {0,3,2}, {1,2,3} };

			const PxTetrahedronMeshGeometry& tetGeom = static_cast<const PxTetrahedronMeshGeometry&>(geom);

			const PxTetrahedronMesh& mesh = *tetGeom.tetrahedronMesh;

			//Get the deformed vertices			
			const PxVec3* vertices = mesh.getVertices();
			const PxU32 tetCount = mesh.getNbTetrahedrons();
			const PxU32 has16BitIndices = mesh.getTetrahedronMeshFlags() & PxTetrahedronMeshFlag::e16_BIT_INDICES;
			const void* indexBuffer = mesh.getTetrahedrons();

			prepareVertexBuffer();

			const PxU32* intIndices = reinterpret_cast<const PxU32*>(indexBuffer);
			const PxU16* shortIndices = reinterpret_cast<const PxU16*>(indexBuffer);
			PxU32 numTotalTriangles = 0;
			for (PxU32 i = 0; i < tetCount; ++i)
			{
				PxU32 vref[4];
				if (has16BitIndices)
				{
					vref[0] = *shortIndices++;
					vref[1] = *shortIndices++;
					vref[2] = *shortIndices++;
					vref[3] = *shortIndices++;
				}
				else
				{
					vref[0] = *intIndices++;
					vref[1] = *intIndices++;
					vref[2] = *intIndices++;
					vref[3] = *intIndices++;
				}

				for (PxU32 j = 0; j < 4; ++j)
				{
					const PxVec3& v0 = vertices[vref[tetFaces[j][0]]];
					const PxVec3& v1 = vertices[vref[tetFaces[j][1]]];
					const PxVec3& v2 = vertices[vref[tetFaces[j][2]]];
					
					PxVec3 fnormal = (v1 - v0).cross(v2 - v0);
					fnormal.normalize();

					pushVertex(v0, v1, v2, fnormal);
					numTotalTriangles++;
				}
			}
			glPushMatrix();
			glScalef(1.0f, 1.0f, 1.0f);
			glEnableClientState(GL_NORMAL_ARRAY);
			glEnableClientState(GL_VERTEX_ARRAY);
			const PxVec3* vertexBuffer = getVertexBuffer();
			glNormalPointer(GL_FLOAT, 2 * 3 * sizeof(float), vertexBuffer);
			glVertexPointer(3, GL_FLOAT, 2 * 3 * sizeof(float), vertexBuffer + 1);
			glDrawArrays(GL_TRIANGLES, 0, int(numTotalTriangles * 3));
			glDisableClientState(GL_VERTEX_ARRAY);
			glDisableClientState(GL_NORMAL_ARRAY);
			glPopMatrix();
		}
		break;

		default:
			break;
	}

	if (gWireFrame)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

static Snippets::Camera* gCamera = NULL;
static KeyboardCallback gKbCb = NULL;

static PxU32 gScreenWidth	= 0;
static PxU32 gScreenHeight	= 0;

static void defaultReshapeCallback(int width, int height)
{
	glViewport(0, 0, width, height);
	gTexter.setScreenResolution(width, height);
	gScreenWidth = width;
	gScreenHeight = height;
}

static void defaultIdleCallback()
{
	glutPostRedisplay();
}

static bool gMoveCamera = false;

static void defaultMotionCallback(int x, int y)
{
	if(gCamera && gMoveCamera)
		gCamera->handleMotion(x, y);
}

static void defaultMouseCallback(int button, int state, int x, int y)
{
	if(button==0)
		gMoveCamera = state==0;

	if(gCamera)
		gCamera->handleMouse(button, state, x, y);
}

static void defaultKeyboardCallback(unsigned char key, int x, int y)
{
	if(key==27)
		glutLeaveMainLoop();

	if (key == 110) //n
		gWireFrame = !gWireFrame;

	const bool status = gCamera ? gCamera->handleKey(key, x, y) : false;
	if(!status && gKbCb)
		gKbCb(key, gCamera ? gCamera->getTransform() : PxTransform(PxIdentity));
}

static void defaultSpecialCallback(int key, int x, int y)
{
	switch(key)
	{
		case GLUT_KEY_UP:		key='W'; break;
		case GLUT_KEY_DOWN:		key='S'; break;
		case GLUT_KEY_LEFT:		key='A'; break;
		case GLUT_KEY_RIGHT:	key='D'; break;
	}

	const bool status = gCamera ? gCamera->handleKey((unsigned char)key, x, y) : false;
	if(!status && gKbCb)
		gKbCb((unsigned char)key, gCamera ? gCamera->getTransform() : PxTransform(PxIdentity));
}

static ExitCallback gUserExitCB = NULL;
static void defaultExitCallback()
{
	releaseVertexBuffer();

	if(gUserExitCB)
		(gUserExitCB)();
}

static void setupDefaultWindow(const char* name, RenderCallback rdcb)
{
	char* namestr = new char[strlen(name)+1];
	strcpy(namestr, name);
	int argc = 1;
	char* argv[1] = { namestr };

	glutInit(&argc, argv);
	
	gScreenWidth	= INITIAL_SCREEN_WIDTH;
	gScreenHeight	= INITIAL_SCREEN_HEIGHT;

	gTexter.init();
	gTexter.setScreenResolution(gScreenWidth, gScreenHeight);
	gTexter.setColor(1.0f, 1.0f, 1.0f, 1.0f);

	glutInitWindowSize(gScreenWidth, gScreenHeight);
	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
	int mainHandle = glutCreateWindow(name);
	GLenum err = glewInit();
	if (GLEW_OK != err)
	{		
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
	}
	glutSetWindow(mainHandle);
	glutReshapeFunc(defaultReshapeCallback);
	glutIdleFunc(defaultIdleCallback);
	glutKeyboardFunc(defaultKeyboardCallback);
	glutSpecialFunc(defaultSpecialCallback);
	glutMouseFunc(defaultMouseCallback);
	glutMotionFunc(defaultMotionCallback);
	if(rdcb)
		glutDisplayFunc(rdcb);
	defaultMotionCallback(0,0);

	delete[] namestr;
}

static void setupDefaultRenderState()
{
	// Setup default render states
//	glClearColor(0.3f, 0.4f, 0.5f, 1.0);
	glClearColor(0.2f, 0.2f, 0.2f, 1.0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	// Setup lighting
/*	glEnable(GL_LIGHTING);
	PxReal ambientColor[]	= { 0.0f, 0.1f, 0.2f, 0.0f };
	PxReal diffuseColor[]	= { 1.0f, 1.0f, 1.0f, 0.0f };		
	PxReal specularColor[]	= { 0.0f, 0.0f, 0.0f, 0.0f };		
	PxReal position[]		= { 100.0f, 100.0f, 400.0f, 1.0f };		
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor);
	glLightfv(GL_LIGHT0, GL_POSITION, position);
	glEnable(GL_LIGHT0);*/
}

static void InitLighting()
{
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_NORMALIZE);

	const float zero[] = { 0.0f, 0.0f, 0.0f, 0.0f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, zero);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, zero);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, zero);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, zero);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);

	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, zero);

	glEnable(GL_LIGHTING);
	PxVec3 Dir(-1.0f, 1.0f, 0.5f);
//	PxVec3 Dir(0.0f, 1.0f, 0.0f);
	Dir.normalize();

	const float AmbientValue = 0.3f;
	const float ambientColor0[]		= { AmbientValue, AmbientValue, AmbientValue, 0.0f };
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor0);

	const float specularColor0[]	= { 0.0f, 0.0f, 0.0f, 0.0f };
	glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor0);

	const float diffuseColor0[]	= { 1.0f, 1.0f, 1.0f, 0.0f };
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor0);

	const float position0[]		= { Dir.x, Dir.y, Dir.z, 0.0f };
	glLightfv(GL_LIGHT0, GL_POSITION, position0);

	glEnable(GL_LIGHT0);

//	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
//	glColor4f(0.0f, 0.0f, 0.0f, 0.0f);

/*		glEnable(GL_LIGHTING);
		PxReal ambientColor[]	= { 0.0f, 0.1f, 0.2f, 0.0f };
		PxReal diffuseColor[]	= { 1.0f, 1.0f, 1.0f, 0.0f };		
		PxReal specularColor[]	= { 0.0f, 0.0f, 0.0f, 0.0f };		
		PxReal position[]		= { 100.0f, 100.0f, 400.0f, 1.0f };		
		glLightfv(GL_LIGHT0, GL_AMBIENT, ambientColor);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor);
		glLightfv(GL_LIGHT0, GL_SPECULAR, specularColor);
		glLightfv(GL_LIGHT0, GL_POSITION, position);
		glEnable(GL_LIGHT0);*/

	if(0)
	{
		glEnable(GL_FOG);
		glFogi(GL_FOG_MODE,GL_LINEAR); 
		//glFogi(GL_FOG_MODE,GL_EXP); 
		//glFogi(GL_FOG_MODE,GL_EXP2); 
		glFogf(GL_FOG_START, 0.0f);
		glFogf(GL_FOG_END, 100.0f);
		glFogf(GL_FOG_DENSITY, 0.005f);
//		glClearColor(0.2f, 0.2f, 0.2f, 1.0);
//		const PxVec3 FogColor(0.2f, 0.2f, 0.2f);
		const PxVec3 FogColor(0.3f, 0.4f, 0.5f);
//		const PxVec3 FogColor(1.0f);
		glFogfv(GL_FOG_COLOR, &FogColor.x);
	}
}

namespace Snippets
{
void initFPS()
{
	gLastTime = glutGet(GLUT_ELAPSED_TIME);
}

void showFPS(int updateIntervalMS, const char* info)
{
	++gFrameCounter;
	int currentTime = glutGet(GLUT_ELAPSED_TIME);
	if (currentTime - gLastTime > updateIntervalMS)
	{
		if (info)
			sprintf(gTitle, " FPS : %4.0f%s", gFrameCounter * 1000.0 / (currentTime - gLastTime), info);
		else
			sprintf(gTitle, " FPS : %4.0f", gFrameCounter * 1000.0 / (currentTime - gLastTime));
		glutSetWindowTitle(gTitle);
		gLastTime = currentTime;
		gFrameCounter = 0;
	}
}

PxU32 getScreenWidth()
{
	return gScreenWidth;
}

PxU32 getScreenHeight()
{
	return gScreenHeight;
}

void enableVSync(bool vsync)
{
#if PX_WIN32 || PX_WIN64
	typedef void (APIENTRY * PFNWGLSWAPINTERVALPROC) (GLenum interval);
	PFNWGLSWAPINTERVALPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALPROC)wglGetProcAddress("wglSwapIntervalEXT");
	wglSwapIntervalEXT(vsync);
#else
	PX_UNUSED(vsync);
#endif
}

void setupDefault(const char* name, Camera* camera, KeyboardCallback kbcb, RenderCallback rdcb, ExitCallback excb)
{
	gCamera = camera;
	gKbCb = kbcb;
	setupDefaultWindow(name, rdcb);
	setupDefaultRenderState();
	enableVSync(true);

	glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_GLUTMAINLOOP_RETURNS);


	gUserExitCB = excb;
	atexit(defaultExitCallback);
}

Camera* getCamera()
{
	return gCamera;
}

static float	gNearClip = 0.0f;
static float	gFarClip = 0.0f;
static float	gFOV = 60.0f;

static float	gTextScale = 0.0f;
static float	gTextY = 0.0f;

PxVec3 computeWorldRayF(float xs, float ys, const PxVec3& camDir)
{
	const float Width = float(gScreenWidth);
	const float Height = float(gScreenHeight);

	// Recenter coordinates in camera space ([-1, 1])
	const float u = ((xs - Width*0.5f)/Width)*2.0f;
	const float v = -((ys - Height*0.5f)/Height)*2.0f;

	// Adjust coordinates according to camera aspect ratio
	const float HTan = tanf(0.25f * fabsf(PxDegToRad(gFOV * 2.0f)));
	const float VTan = HTan*(Width/Height);

	// Ray in camera space
	const PxVec3 CamRay(VTan*u, HTan*v, 1.0f);

	// Compute ray in world space
	PxVec3 Right, Up;
	PxComputeBasisVectors(camDir, Right, Up);

	const PxMat33 invView(-Right, Up, camDir);

	return invView.transform(CamRay).getNormalized();
}

void startRender(const Camera* camera, float clipNear, float clipFar, float fov, bool setupLighting)
{
	const PxVec3 cameraEye = camera->getEye();
	const PxVec3 cameraDir = camera->getDir();

	gNearClip = clipNear;
	gFarClip = clipFar;
	gFOV = fov;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Setup camera
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(GLdouble(fov), GLdouble(glutGet(GLUT_WINDOW_WIDTH)) / GLdouble(glutGet(GLUT_WINDOW_HEIGHT)), GLdouble(clipNear), GLdouble(clipFar));

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(GLdouble(cameraEye.x), GLdouble(cameraEye.y), GLdouble(cameraEye.z), GLdouble(cameraEye.x + cameraDir.x), GLdouble(cameraEye.y + cameraDir.y), GLdouble(cameraEye.z + cameraDir.z), 0.0, 1.0, 0.0);

	glColor4f(0.4f, 0.4f, 0.4f, 1.0f);

	if(setupLighting)
		InitLighting();

	gTextScale = 0.0175f * float(INITIAL_SCREEN_HEIGHT) / float(gScreenHeight);
	gTextY = 1.0f - gTextScale;
	gTexter.setColor(1.0f, 1.0f, 1.0f, 1.0f);
}

void finishRender()
{
	glutSwapBuffers();
}

void print(const char* text)
{
	gTexter.print(0.0f, gTextY, gTextScale, text);
	gTextY -= gTextScale;
}

const PxVec3 shadowDir(0.0f, -0.7071067f, -0.7071067f);
const PxReal shadowMat[] = { 1,0,0,0, -shadowDir.x / shadowDir.y,0,-shadowDir.z / shadowDir.y,0, 0,0,1,0, 0,0,0,1 };

void renderDeformableVolume(PxDeformableVolume* deformableVolume, const PxVec4* deformedPositionsInvMass, bool shadows, const PxVec3& color)
{
	PxShape* shape = deformableVolume->getShape();

	const PxMat44 shapePose(PxIdentity); // (PxShapeExt::getGlobalPose(*shapes[j], *actors[i]));
	const PxGeometry& geom = shape->getGeometry();

	const PxTetrahedronMeshGeometry& tetGeom = static_cast<const PxTetrahedronMeshGeometry&>(geom);

	const PxTetrahedronMesh& mesh = *tetGeom.tetrahedronMesh;

	glPushMatrix();
	glMultMatrixf(&shapePose.column0.x);
	glColor4f(color.x, color.y, color.z, 1.0f);
	renderDeformableVolumeGeometry(mesh, deformedPositionsInvMass);
	glPopMatrix();

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	if (shadows)
	{
		glPushMatrix();
		glMultMatrixf(shadowMat);
		glMultMatrixf(&shapePose.column0.x);
		glDisable(GL_LIGHTING);
		//glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
		glColor4f(0.1f, 0.1f, 0.1f, 1.0f);
		renderDeformableVolumeGeometry(mesh, deformedPositionsInvMass);
		glEnable(GL_LIGHTING);
		glPopMatrix();
	}
}


void renderActors(PxRigidActor** actors, const PxU32 numActors, bool shadows, const PxVec3& color, TriggerRender* cb,
	bool changeColorForSleepingActors, bool wireframePass)
{
	PxShape* shapes[MAX_NUM_ACTOR_SHAPES];
	for(PxU32 i=0;i<numActors;i++)
	{
		const PxU32 nbShapes = actors[i]->getNbShapes();
		PX_ASSERT(nbShapes <= MAX_NUM_ACTOR_SHAPES);
		actors[i]->getShapes(shapes, nbShapes);
		bool sleeping;
		if (changeColorForSleepingActors)
			sleeping = actors[i]->is<PxRigidDynamic>() ? actors[i]->is<PxRigidDynamic>()->isSleeping() : false;
		else
			sleeping = false;

		for(PxU32 j=0;j<nbShapes;j++)
		{
			const PxMat44 shapePose(PxShapeExt::getGlobalPose(*shapes[j], *actors[i]));
			const PxGeometry& geom = shapes[j]->getGeometry();

			const bool isTrigger = cb ? cb->isTrigger(shapes[j]) : shapes[j]->getFlags() & PxShapeFlag::eTRIGGER_SHAPE;
			if(isTrigger)
				glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			
			// render object
			glPushMatrix();						
			glMultMatrixf(&shapePose.column0.x);
			if(sleeping)
			{
				const PxVec3 darkColor = color * 0.25f;
				glColor4f(darkColor.x, darkColor.y, darkColor.z, 1.0f);
			}
			else
				glColor4f(color.x, color.y, color.z, 1.0f);
			renderGeometry(geom);
			glPopMatrix();

			glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

			if(shadows)
			{
				glPushMatrix();						
				glMultMatrixf(shadowMat);
				glMultMatrixf(&shapePose.column0.x);
				glDisable(GL_LIGHTING);
				//glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
				glColor4f(0.1f, 0.1f, 0.1f, 1.0f);
				renderGeometry(geom);
				glEnable(GL_LIGHTING);
				glPopMatrix();
			}
		}
	}

	if(wireframePass)
	{
		const GLdouble aspect = GLdouble(glutGet(GLUT_WINDOW_WIDTH)) / GLdouble(glutGet(GLUT_WINDOW_HEIGHT));
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, aspect, GLdouble(gNearClip*1.005f), GLdouble(gFarClip));
		glMatrixMode(GL_MODELVIEW);

		glDisable(GL_LIGHTING);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
		for(PxU32 i=0;i<numActors;i++)
		{
			const PxU32 nbShapes = actors[i]->getNbShapes();
			PX_ASSERT(nbShapes <= MAX_NUM_ACTOR_SHAPES);
			actors[i]->getShapes(shapes, nbShapes);

			for(PxU32 j=0;j<nbShapes;j++)
			{
				const PxMat44 shapePose(PxShapeExt::getGlobalPose(*shapes[j], *actors[i]));
				glPushMatrix();						
				glMultMatrixf(&shapePose.column0.x);
				renderGeometry(shapes[j]->getGeometry());
				glPopMatrix();
			}
		}
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_LIGHTING);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, aspect, GLdouble(gNearClip), GLdouble(gFarClip));
		glMatrixMode(GL_MODELVIEW);
	}
}

/*static const PxU32 gGeomSizes[] = {
	sizeof(PxSphereGeometry),
	sizeof(PxPlaneGeometry),
	sizeof(PxCapsuleGeometry),
	sizeof(PxBoxGeometry),
	sizeof(PxConvexMeshGeometry),
	sizeof(PxTriangleMeshGeometry),
	sizeof(PxHeightFieldGeometry),
};

void renderGeoms(const PxU32 nbGeoms, const PxGeometry* geoms, const PxTransform* poses, bool shadows, const PxVec3& color)
{
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	const PxVec3 shadowDir(0.0f, -0.7071067f, -0.7071067f);
	const PxReal shadowMat[]={ 1,0,0,0, -shadowDir.x/shadowDir.y,0,-shadowDir.z/shadowDir.y,0, 0,0,1,0, 0,0,0,1 };

	const PxU8* stream = reinterpret_cast<const PxU8*>(geoms);
	for(PxU32 j=0;j<nbGeoms;j++)
	{
		const PxMat44 shapePose(poses[j]);

		const PxGeometry& geom = *reinterpret_cast<const PxGeometry*>(stream);
		stream += gGeomSizes[geom.getType()];

		// render object
		glPushMatrix();						
		glMultMatrixf(&shapePose.column0.x);
		glColor4f(color.x, color.y, color.z, 1.0f);
		renderGeometry(geom);
		glPopMatrix();

		if(shadows)
		{
			glPushMatrix();						
			glMultMatrixf(shadowMat);
			glMultMatrixf(&shapePose.column0.x);
			glDisable(GL_LIGHTING);
			glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
			renderGeometry(geom);
			glEnable(GL_LIGHTING);
			glPopMatrix();
		}
	}
}*/

void renderGeoms(const PxU32 nbGeoms, const PxGeometryHolder* geoms, const PxTransform* poses, bool shadows, const PxVec3& color)
{
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	//const PxVec3 shadowDir(0.0f, -0.7071067f, -0.7071067f);
	//const PxReal shadowMat[]={ 1,0,0,0, -shadowDir.x/shadowDir.y,0,-shadowDir.z/shadowDir.y,0, 0,0,1,0, 0,0,0,1 };

	for(PxU32 j=0;j<nbGeoms;j++)
	{
		const PxMat44 shapePose(poses[j]);

		const PxGeometry& geom = geoms[j].any();

		// render object
		glPushMatrix();						
		glMultMatrixf(&shapePose.column0.x);
		glColor4f(color.x, color.y, color.z, 1.0f);
		renderGeometry(geom);
		glPopMatrix();

		if(shadows)
		{
			glPushMatrix();						
			glMultMatrixf(shadowMat);
			glMultMatrixf(&shapePose.column0.x);
			glDisable(GL_LIGHTING);
			//glColor4f(0.1f, 0.2f, 0.3f, 1.0f);
			glColor4f(0.1f, 0.1f, 0.1f, 1.0f);
			renderGeometry(geom);
			glEnable(GL_LIGHTING);
			glPopMatrix();
		}
	}

	if(1)
	{
		const GLdouble aspect = GLdouble(glutGet(GLUT_WINDOW_WIDTH)) / GLdouble(glutGet(GLUT_WINDOW_HEIGHT));
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, aspect, GLdouble(gNearClip*1.005f), GLdouble(gFarClip));
		glMatrixMode(GL_MODELVIEW);

		glDisable(GL_LIGHTING);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glColor4f(0.0f, 0.0f, 0.0f, 1.0f);


		for(PxU32 j=0;j<nbGeoms;j++)
		{
			const PxMat44 shapePose(poses[j]);

			const PxGeometry& geom = geoms[j].any();

			// render object
			glPushMatrix();						
			glMultMatrixf(&shapePose.column0.x);
			renderGeometry(geom);
			glPopMatrix();
		}


		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_LIGHTING);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, aspect, GLdouble(gNearClip), GLdouble(gFarClip));
		glMatrixMode(GL_MODELVIEW);
	}
}

PX_FORCE_INLINE PxVec3 getVec3(const physx::PxU8* data, const PxU32 index, const PxU32 sStrideInBytes)
{
	return *reinterpret_cast<const PxVec3*>(data + index * sStrideInBytes);
}

void renderMesh(physx::PxU32 /*nbVerts*/, const physx::PxU8* verts, const PxU32 vertsStrideInBytes, physx::PxU32 nbTris, const void* indices, bool has16bitIndices, const physx::PxVec3& color,
	const physx::PxU8* normals, const PxU32 normalsStrideInBytes, bool flipFaceOrientation, bool enableBackFaceCulling = true)
{
	if (nbTris == 0)
		return;

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	const PxMat44 idt(PxIdentity);
	glPushMatrix();						
	glMultMatrixf(&idt.column0.x);
	glColor4f(color.x, color.y, color.z, 1.0f);
	if (!enableBackFaceCulling)
		glDisable(GL_CULL_FACE);
	{
		prepareVertexBuffer();

		PxU32 numTotalTriangles = 0;
		PX_UNUSED(numTotalTriangles);

		for(PxU32 i=0; i <nbTris; ++i)
		{
			PxU32 vref0, vref1, vref2;
			if (has16bitIndices)
			{
				vref0 = ((const PxU16*)indices)[i * 3 + 0];
				vref1 = ((const PxU16*)indices)[i * 3 + 1];
				vref2 = ((const PxU16*)indices)[i * 3 + 2];
			}
			else
			{
				vref0 = ((const PxU32*)indices)[i * 3 + 0];
				vref1 = ((const PxU32*)indices)[i * 3 + 1];
				vref2 = ((const PxU32*)indices)[i * 3 + 2];
			}

			const PxVec3& v0 = getVec3(verts, vref0, vertsStrideInBytes);
			const PxVec3& v1 = flipFaceOrientation ? getVec3(verts, vref2, vertsStrideInBytes) : getVec3(verts, vref1, vertsStrideInBytes);
			const PxVec3& v2 = flipFaceOrientation ? getVec3(verts, vref1, vertsStrideInBytes) : getVec3(verts, vref2, vertsStrideInBytes);
			
			if (normals)
			{
				const PxVec3& n0 = getVec3(normals, vref0, normalsStrideInBytes);
				const PxVec3& n1 = flipFaceOrientation ? getVec3(normals, vref2, normalsStrideInBytes) : getVec3(normals, vref1, normalsStrideInBytes);
				const PxVec3& n2 = flipFaceOrientation ? getVec3(normals, vref1, normalsStrideInBytes) : getVec3(normals, vref2, normalsStrideInBytes);
				pushVertex(v0, v1, v2, n0, n1, n2, flipFaceOrientation ? -1.0f : 1.0f);
			}
			else 
			{
				PxVec3 fnormal = (v1 - v0).cross(v2 - v0);
				fnormal.normalize();
				pushVertex(v0, v1, v2, fnormal);
			}
			numTotalTriangles++;
		}
		glScalef(1.0f, 1.0f, 1.0f);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);
		const PxVec3* vertexBuffer = getVertexBuffer();
		glNormalPointer(GL_FLOAT, 2*3*sizeof(float), vertexBuffer);
		glVertexPointer(3, GL_FLOAT, 2*3*sizeof(float), vertexBuffer+1);
		glDrawArrays(GL_TRIANGLES, 0, int(nbTris * 3));
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
	}
	glPopMatrix();
	if (!enableBackFaceCulling)
		glEnable(GL_CULL_FACE);

	if(0)
	{
		const GLdouble aspect = GLdouble(glutGet(GLUT_WINDOW_WIDTH)) / GLdouble(glutGet(GLUT_WINDOW_HEIGHT));
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, aspect, GLdouble(gNearClip*1.005f), GLdouble(gFarClip));
		glMatrixMode(GL_MODELVIEW);

		glDisable(GL_LIGHTING);
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		glColor4f(0.0f, 0.0f, 0.0f, 1.0f);

			glPushMatrix();						
			glMultMatrixf(&idt.column0.x);

				glEnableClientState(GL_NORMAL_ARRAY);
				glEnableClientState(GL_VERTEX_ARRAY);
				const PxVec3* vertexBuffer = getVertexBuffer();
				glNormalPointer(GL_FLOAT, 2*3*sizeof(float), vertexBuffer);
				glVertexPointer(3, GL_FLOAT, 2*3*sizeof(float), vertexBuffer+1);				
				glDrawArrays(GL_TRIANGLES, 0, int(nbTris * 3));
				glDisableClientState(GL_VERTEX_ARRAY);
				glDisableClientState(GL_NORMAL_ARRAY);

			glPopMatrix();

		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_LIGHTING);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60.0, aspect, GLdouble(gNearClip), GLdouble(gFarClip));
		glMatrixMode(GL_MODELVIEW);
	}
}

void renderMesh(physx::PxU32 nbVerts, const physx::PxVec3* verts, physx::PxU32 nbTris, const physx::PxU32* indices, const physx::PxVec3& color, const physx::PxVec3* normals, bool flipFaceOrientation)
{
	renderMesh(nbVerts, reinterpret_cast<const PxU8*>(verts), sizeof(PxVec3), nbTris, indices, false, color, reinterpret_cast<const PxU8*>(normals), sizeof(PxVec3), flipFaceOrientation);
}

void renderMesh(physx::PxU32 nbVerts, const physx::PxVec4* verts, physx::PxU32 nbTris, const physx::PxU32* indices, const physx::PxVec3& color, const physx::PxVec4* normals, bool flipFaceOrientation)
{
	renderMesh(nbVerts, reinterpret_cast<const PxU8*>(verts), sizeof(PxVec4), nbTris, indices, false, color, reinterpret_cast<const PxU8*>(normals), sizeof(PxVec4), flipFaceOrientation);
}

void renderMesh(physx::PxU32 nbVerts, const physx::PxVec4* verts, physx::PxU32 nbTris, const void* indices, bool hasSixteenBitIndices,
	const physx::PxVec3& color, const physx::PxVec4* normals, bool flipFaceOrientation, bool enableBackFaceCulling)
{
	renderMesh(nbVerts, reinterpret_cast<const PxU8*>(verts), sizeof(PxVec4), nbTris, indices, hasSixteenBitIndices,
		color, reinterpret_cast<const PxU8*>(normals), sizeof(PxVec4), flipFaceOrientation, enableBackFaceCulling);
}

void DrawLine(const PxVec3& p0, const PxVec3& p1, const PxVec3& color)
{
	glDisable(GL_LIGHTING);
	glColor4f(color.x, color.y, color.z, 1.0f);
	const PxVec3 Pts[] = { p0, p1 };
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(PxVec3), &Pts[0].x);
	glDrawArrays(GL_LINES, 0, 2);
	glDisableClientState(GL_VERTEX_ARRAY);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

const physx::PxVec3 icosahedronPoints[12] = { PxVec3(0, -0.525731, 0.850651),
PxVec3(0.850651, 0, 0.525731),
PxVec3(0.850651, 0, -0.525731),
PxVec3(-0.850651, 0, -0.525731),
PxVec3(-0.850651, 0, 0.525731),
PxVec3(-0.525731, 0.850651, 0),
PxVec3(0.525731, 0.850651, 0),
PxVec3(0.525731, -0.850651, 0),
PxVec3(-0.525731, -0.850651, 0),
PxVec3(0, -0.525731, -0.850651),
PxVec3(0, 0.525731, -0.850651),
PxVec3(0, 0.525731, 0.850651) };
const PxU32 icosahedronIndices[3 * 20] = { 1  ,2  ,6  ,
1  ,7  ,2  ,
3  ,4  ,5  ,
4  ,3  ,8  ,
6  ,5  ,11 ,
5  ,6  ,10 ,
9  ,10 ,2  ,
10 ,9  ,3  ,
7  ,8  ,9  ,
8  ,7  ,0  ,
11 ,0  ,1  ,
0  ,11 ,4  ,
6  ,2  ,10 ,
1  ,6  ,11 ,
3  ,5  ,10 ,
5  ,4  ,11 ,
2  ,7  ,9  ,
7  ,1  ,0  ,
3  ,9  ,8  ,
4  ,8  ,0 };

void DrawIcosahedraPoints(const PxArray<PxVec3>& pts, const PxVec3& color, float radius)
{
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	const PxMat44 idt(PxIdentity);
	glPushMatrix();
	glMultMatrixf(&idt.column0.x);
	glColor4f(color.x, color.y, color.z, 1.0f);
	PxU32 numTotalTriangles = 0;
	{
		prepareVertexBuffer();

		for (PxU32 i = 0; i < pts.size(); ++i)
		{
			PxVec3 center = pts[i];

			for (PxU32 j = 0; j < 20; ++j) 
			{
				const PxVec3& v0 = icosahedronPoints[icosahedronIndices[3 * j + 0]] * radius + center;
				const PxVec3& v1 = icosahedronPoints[icosahedronIndices[3 * j + 1]] * radius + center;
				const PxVec3& v2 = icosahedronPoints[icosahedronIndices[3 * j + 2]] * radius + center;

				{
					PxVec3 fnormal = (v1 - v0).cross(v2 - v0);
					fnormal.normalize();
					pushVertex(v0, v1, v2, fnormal);
				}
				numTotalTriangles++;
			}
		}
		glScalef(1.0f, 1.0f, 1.0f);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);
		const PxVec3* vertexBuffer = getVertexBuffer();
		glNormalPointer(GL_FLOAT, 2 * 3 * sizeof(float), vertexBuffer);
		glVertexPointer(3, GL_FLOAT, 2 * 3 * sizeof(float), vertexBuffer + 1);
		glDrawArrays(GL_TRIANGLES, 0, int(numTotalTriangles * 3));
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_NORMAL_ARRAY);
	}
	glPopMatrix();
}

void DrawPoints(const PxArray<PxVec3>& pts, const PxVec3& color, float scale)
{
	glPointSize(scale);
	glDisable(GL_LIGHTING);
	glColor4f(color.x, color.y, color.z, 1.0f);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(PxVec3), pts.begin());
	glDrawArrays(GL_POINTS, 0, pts.size());
	glDisableClientState(GL_VERTEX_ARRAY);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

void DrawPoints(const PxArray<PxVec4>& pts, const PxVec3& color, float scale)
{
	glPointSize(scale);
	glDisable(GL_LIGHTING);
	glColor4f(color.x, color.y, color.z, 1.0f);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(PxVec4), pts.begin());
	glDrawArrays(GL_POINTS, 0, pts.size());
	glDisableClientState(GL_VERTEX_ARRAY);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

void DrawPoints(const physx::PxArray<physx::PxVec3>& pts, const physx::PxArray<physx::PxVec3>& colors, float scale)
{
	glPointSize(scale);
	glDisable(GL_LIGHTING);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(PxVec3), pts.begin());
	glColorPointer(3, GL_FLOAT, sizeof(PxVec3), colors.begin());
	glDrawArrays(GL_POINTS, 0, pts.size());
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

void DrawPoints(const physx::PxArray<physx::PxVec4>& pts, const physx::PxArray<physx::PxVec3>& colors, float scale)
{
	glPointSize(scale);
	glDisable(GL_LIGHTING);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);
	glVertexPointer(3, GL_FLOAT, sizeof(PxVec4), pts.begin());
	glColorPointer(3, GL_FLOAT, sizeof(PxVec3), colors.begin());
	glDrawArrays(GL_POINTS, 0, pts.size());
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

#if PX_SUPPORT_GPU_PHYSX

namespace
{
	void createVBO(GLuint* vbo, PxU32 size)
	{
		PX_ASSERT(vbo);

		// create buffer object
		glGenBuffers(1, vbo);
		glBindBuffer(GL_ARRAY_BUFFER, *vbo);

		// initialize buffer object
		glBufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	void deleteVBO(GLuint* vbo)
	{
		glBindBuffer(1, *vbo);
		glDeleteBuffers(1, vbo);
		*vbo = 0;
	}

#if USE_CUDA_INTEROP
	//Returns the pointer to the cuda buffer
	void* mapCudaGraphicsResource(CUgraphicsResource* vbo_resource, size_t& numBytes, CUstream stream = 0)
	{
		CUresult result0 = cuGraphicsMapResources(1, vbo_resource, stream);
		PX_UNUSED(result0);
		void* dptr;
		CUresult result1 = cuGraphicsResourceGetMappedPointer((CUdeviceptr*)&dptr, &numBytes, *vbo_resource);
		PX_UNUSED(result1);
		return dptr;
	}

	void unmapCudaGraphicsResource(CUgraphicsResource* vbo_resource, CUstream stream = 0)
	{
		CUresult result2 = cuGraphicsUnmapResources(1, vbo_resource, stream);
		PX_UNUSED(result2);
	}
#endif

} // namespace

SharedGLBuffer::SharedGLBuffer() : vbo_res(NULL), devicePointer(NULL), vbo(0), size(0)
{
}

void SharedGLBuffer::initialize(PxCudaContextManager* contextManager)
{
	cudaContextManager = contextManager;
}

void SharedGLBuffer::allocate(PxU32 sizeInBytes)
{
	release();
	createVBO(&vbo, sizeInBytes);
#if USE_CUDA_INTEROP
	physx::PxCudaInteropRegisterFlags flags = physx::PxCudaInteropRegisterFlags();
	cudaContextManager->acquireContext();
	CUresult result = cuGraphicsGLRegisterBuffer(reinterpret_cast<CUgraphicsResource*>(&vbo_res), vbo, flags);
	PX_UNUSED(result);
	cudaContextManager->releaseContext();
#endif
	size = sizeInBytes;
}

void SharedGLBuffer::release()
{
	if (vbo)
	{
		deleteVBO(&vbo);
		vbo = 0;
	}
#if USE_CUDA_INTEROP
	if (vbo_res)
	{
		cudaContextManager->acquireContext();
		CUresult result = cuGraphicsUnregisterResource(reinterpret_cast<CUgraphicsResource>(vbo_res));
		PX_UNUSED(result);
		cudaContextManager->releaseContext();
		vbo_res = NULL;
	}
#endif
}

SharedGLBuffer::~SharedGLBuffer()
{
	//release();
}

void* SharedGLBuffer::map()
{
	if (devicePointer)
		return devicePointer;
	
#if USE_CUDA_INTEROP
	size_t numBytes;
	cudaContextManager->acquireContext();
	devicePointer = mapCudaGraphicsResource(reinterpret_cast<CUgraphicsResource*>(&vbo_res), numBytes, 0);
	cudaContextManager->releaseContext();
#else
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	devicePointer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
#endif
	return devicePointer;
}

void SharedGLBuffer::unmap()
{
	if (!devicePointer)
		return;
#if USE_CUDA_INTEROP
	cudaContextManager->acquireContext();
	unmapCudaGraphicsResource(reinterpret_cast<CUgraphicsResource*>(&vbo_res), 0);
	cudaContextManager->releaseContext();
#else
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glUnmapBuffer(GL_ARRAY_BUFFER);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
#endif
	devicePointer = NULL;
}

#endif // PX_SUPPORT_GPU_PHYSX

void DrawPoints(GLuint vbo, PxU32 numPoints, const PxVec3& color, float scale, PxU32 coordinatesPerPoint, PxU32 stride, size_t offset)
{
	glPointSize(scale);	
	glDisable(GL_LIGHTING);
	glColor4f(color.x, color.y, color.z, 1.0f);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(coordinatesPerPoint, GL_FLOAT, stride, (void*)offset /*offsetof(Vertex, pos)*/);
	glDrawArrays(GL_POINTS, 0, numPoints);
	glDisableClientState(GL_VERTEX_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

void DrawLines(GLuint vbo, PxU32 numPoints, const PxVec3& color, float scale, PxU32 coordinatesPerPoint, PxU32 stride, size_t offset)
{
	PX_ASSERT(numPoints % 2 == 0);
	glLineWidth(scale);	
	glDisable(GL_LIGHTING);
	glColor4f(color.x, color.y, color.z, 1.0f);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(coordinatesPerPoint, GL_FLOAT, stride, (void*)offset /*offsetof(Vertex, pos)*/);
	glDrawArrays(GL_LINES, 0, numPoints);
	glDisableClientState(GL_VERTEX_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glEnable(GL_LIGHTING);
}

void DrawMeshIndexed(GLuint vbo, GLuint elementbuffer, GLuint numTriangles, const physx::PxVec3& color, physx::PxU32 stride)
{
	/*glPushMatrix();
	glScalef(1.0f, 1.0f, 1.0f);*/

	if(gWireFrame)
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glColor4f(color.x, color.y, color.z, 1.0f);

	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glEnableClientState(GL_VERTEX_ARRAY);	
	glVertexPointer(3, GL_FLOAT, stride, (void*)0);
	glEnableClientState(GL_NORMAL_ARRAY);
	glNormalPointer(GL_FLOAT, stride, (void*)(3 * sizeof(float)));

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);

	glDrawElements(GL_TRIANGLES, int(numTriangles * 3), GL_UNSIGNED_INT, (void*)0);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	//glPopMatrix();
	if (gWireFrame)
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

void DrawMeshIndexedNoNormals(GLuint vbo, GLuint elementbuffer, GLuint numTriangles, const physx::PxVec3& color, physx::PxU32 stride)
{
	/*glPushMatrix();
	glScalef(1.0f, 1.0f, 1.0f);*/

	glColor4f(color.x, color.y, color.z, 1.0f);

	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, stride, (void*)0);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);

	glDrawElements(GL_TRIANGLES, int(numTriangles * 3), GL_UNSIGNED_INT, (void*)0);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	//glPopMatrix();
}

void DrawFrame(const PxVec3& pt, float scale)
{
	DrawLine(pt, pt + PxVec3(scale, 0.0f, 0.0f), PxVec3(1.0f, 0.0f, 0.0f));
	DrawLine(pt, pt + PxVec3(0.0f, scale, 0.0f), PxVec3(0.0f, 1.0f, 0.0f));
	DrawLine(pt, pt + PxVec3(0.0f, 0.0f, scale), PxVec3(0.0f, 0.0f, 1.0f));
}

void DrawBounds(const physx::PxBounds3& box, const physx::PxVec3& color)
{
	DrawLine(PxVec3(box.minimum.x, box.minimum.y, box.minimum.z), PxVec3(box.maximum.x, box.minimum.y, box.minimum.z), color);
	DrawLine(PxVec3(box.maximum.x, box.minimum.y, box.minimum.z), PxVec3(box.maximum.x, box.maximum.y, box.minimum.z), color);
	DrawLine(PxVec3(box.maximum.x, box.maximum.y, box.minimum.z), PxVec3(box.minimum.x, box.maximum.y, box.minimum.z), color);
	DrawLine(PxVec3(box.minimum.x, box.maximum.y, box.minimum.z), PxVec3(box.minimum.x, box.minimum.y, box.minimum.z), color);
	DrawLine(PxVec3(box.minimum.x, box.minimum.y, box.minimum.z), PxVec3(box.minimum.x, box.minimum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.minimum.x, box.minimum.y, box.maximum.z), PxVec3(box.maximum.x, box.minimum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.maximum.x, box.minimum.y, box.maximum.z), PxVec3(box.maximum.x, box.maximum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.maximum.x, box.maximum.y, box.maximum.z), PxVec3(box.minimum.x, box.maximum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.minimum.x, box.maximum.y, box.maximum.z), PxVec3(box.minimum.x, box.minimum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.minimum.x, box.minimum.y, box.maximum.z), PxVec3(box.minimum.x, box.minimum.y, box.minimum.z), color);
	DrawLine(PxVec3(box.maximum.x, box.minimum.y, box.minimum.z), PxVec3(box.maximum.x, box.minimum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.maximum.x, box.maximum.y, box.minimum.z), PxVec3(box.maximum.x, box.maximum.y, box.maximum.z), color);
	DrawLine(PxVec3(box.minimum.x, box.maximum.y, box.minimum.z), PxVec3(box.minimum.x, box.maximum.y, box.maximum.z), color);
}

void DrawBounds(const PxBounds3& box)
{
	const PxVec3 color(1.0f, 1.0f, 0.0f);
	DrawBounds(box, color);
}

GLuint CreateTexture(PxU32 width, PxU32 height, const GLubyte* buffer, bool createMipmaps)
{
	GLuint texId;
	glGenTextures(1, &texId);

	glBindTexture(GL_TEXTURE_2D, texId);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	if(buffer)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
		if(createMipmaps)
		{
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
			gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
		}
	}
	glBindTexture(GL_TEXTURE_2D, 0);
	return texId;
}

void UpdateTexture(GLuint texId, physx::PxU32 width, physx::PxU32 height, const GLubyte* buffer, bool createMipmaps)
{
	glBindTexture(GL_TEXTURE_2D, texId);
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
	if(createMipmaps)
	{
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
		gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
	}
    glBindTexture( GL_TEXTURE_2D, 0);
}

void ReleaseTexture(GLuint texId)
{
	glDeleteTextures(1, &texId);
}

void DrawRectangle(float x_start, float x_end, float y_start, float y_end, const PxVec3& color_top, const PxVec3& color_bottom, float alpha, PxU32 screen_width, PxU32 screen_height, bool draw_outline, bool texturing)
{
	if(alpha!=1.0f)
	{
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
	}
	else
	{
		glDisable(GL_BLEND);
	}
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_LIGHTING);
	glDisable(GL_CULL_FACE);

	if(texturing)
		glEnable(GL_TEXTURE_2D);
	else
		glDisable(GL_TEXTURE_2D);

	{
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		glOrtho(0, screen_width, 0, screen_height, -1, 1);
	}
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	const float x0 = x_start * float(screen_width);
	const float x1 = x_end * float(screen_width);
	const float y0 = y_start * float(screen_height);
	const float y1 = y_end * float(screen_height);

	const PxVec3 p0(x0,	y0, 0.0f);
	const PxVec3 p1(x0,	y1, 0.0f);
	const PxVec3 p2(x1,	y1, 0.0f);
	const PxVec3 p3(x1,	y0, 0.0f);

//	const float u = (texture_flags & SQT_FLIP_U) ? 1.0f : 0.0f;
//	const float v = (texture_flags & SQT_FLIP_V) ? 1.0f : 0.0f;
	const float u = 0.0f;
	const float v = 1.0f;

	const PxVec3 t0(u, v, 0.0f);
	const PxVec3 t1(u, 1.0f - v, 0.0f);
	const PxVec3 t2(1.0f - u, 1.0f - v, 0.0f);
	const PxVec3 t3(1.0f - u, v, 0.0f);

	glBegin(GL_TRIANGLES);
		glTexCoord2f(t0.x, t0.y);
		glColor4f(color_top.x, color_top.y, color_top.z, alpha);
		glVertex3f(p0.x, p0.y, p0.z);

		glTexCoord2f(t1.x, t1.y);
		glColor4f(color_bottom.x, color_bottom.y, color_bottom.z, alpha);
		glVertex3f(p1.x, p1.y, p1.z);

		glTexCoord2f(t2.x, t2.y);
		glColor4f(color_bottom.x, color_bottom.y, color_bottom.z, alpha);
		glVertex3f(p2.x, p2.y, p2.z);

		glTexCoord2f(t0.x, t0.y);
		glColor4f(color_top.x, color_top.y, color_top.z, alpha);
		glVertex3f(p0.x, p0.y, p0.z);

		glTexCoord2f(t2.x, t2.y);
		glColor4f(color_bottom.x, color_bottom.y, color_bottom.z, alpha);
		glVertex3f(p2.x, p2.y, p2.z);

		glTexCoord2f(t3.x, t3.y);
		glColor4f(color_top.x, color_top.y, color_top.z, alpha);
		glVertex3f(p3.x, p3.y, p3.z);
	glEnd();

	if(draw_outline)
	{
		glDisable(GL_TEXTURE_2D);
		glColor4f(1.0f, 1.0f, 1.0f, alpha);
		glBegin(GL_LINES);
			glVertex3f(p0.x, p0.y, p0.z);
			glVertex3f(p1.x, p1.y, p1.z);

			glVertex3f(p1.x, p1.y, p1.z);
			glVertex3f(p2.x, p2.y, p2.z);

			glVertex3f(p2.x, p2.y, p2.z);
			glVertex3f(p3.x, p3.y, p3.z);

			glVertex3f(p3.x, p3.y, p3.z);
			glVertex3f(p0.x, p0.y, p0.z);
		glEnd();
	}

	{
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
	}
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	
	glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);
	glDisable(GL_BLEND);
}

void DisplayTexture(GLuint texId, PxU32 size, PxU32 margin)
{
	const PxU32 screenWidth = gScreenWidth;
	const PxU32 screenHeight = gScreenHeight;

	glBindTexture(GL_TEXTURE_2D, texId);
//	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
//	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

//	glDisable(GL_CULL_FACE);
	const PxVec3 color(1.0f, 1.0f, 1.0f);
//	const float Alpha = 0.999f;
//	const float Alpha = 0.5f;
	const float Alpha = 1.0f;
	PxU32 PixelSizeX = size;
	PxU32 PixelSizeY = size;

	if(!size)
	{
		PixelSizeX = gScreenWidth;
		PixelSizeY = gScreenHeight;
		margin = 0;
	}

	const float x0 = float(screenWidth-PixelSizeX-margin)/float(screenWidth);
	const float y0 = float(screenHeight-PixelSizeY-margin)/float(screenHeight);
	const float x1 = float(screenWidth-margin)/float(screenWidth);
	const float y1 = float(screenHeight-margin)/float(screenHeight);

	DrawRectangle(x0, x1, y0, y1, color, color, Alpha, screenWidth, screenHeight, false, true);

	glDisable(GL_TEXTURE_2D);
}

} //namespace Snippets

