
// zlib open source license
//
// Copyright (c) 2010 to 2013 David Forsgren Piuva
// 
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// 
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 
//    1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 
//    2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 
//    3. This notice may not be removed or altered from any source
//    distribution.

#define MAKE_COLLISION_CONSTRUCTOR_INTERFACE(NAME,INPUT,OUTPUT) \
int CDFPGECtrl::##NAME##INPUT { \
	int Result; \
	CRITICAL_CODE_SECTION( \
		if (DGE.m_Running) { \
			Result = DGE.IDFromPointer(DGE.##NAME##OUTPUT); \
		} else { \
			REPORT_NOT_RUNNING(L#NAME) \
			Result = 0; \
		} \
	) \
	return Result; \
}
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_Box,(float HalfWidth, float HalfHeight, float HalfDepth),(HalfWidth,HalfHeight,HalfDepth,NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_Sphere,(float Radius),(Radius,NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_StaticPlane,(float NX, float NY, float NZ, float D),(NX,NY,NZ,D,NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_Cylinder,(float HalfWidth, float HalfHeight, float HalfDepth),(HalfWidth,HalfHeight,HalfDepth,NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_Capsule,(float Radius, float HalfHeight),(Radius,HalfHeight * 2,NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_Cone,(float Radius, float HalfHeight),(Radius,HalfHeight * 2,NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_Compound,(void),(NULL))
MAKE_COLLISION_CONSTRUCTOR_INTERFACE(CollisionShape_Create_ConvexHull,(void),(NULL))

// These could not be made with the default macro
int CDFPGECtrl::CollisionShape_Create_HeightField_UsingCPUSurface(int CPUSurface,int UpDimension,int ChannelIndex,float HalfBoundHeight,bool FlipQuadEdges,bool DiamondPattern) {
	int Result;
	CRITICAL_CODE_SECTION(
		Result = 0;
		GET_FROM_REF(CPUSurface,CPUSurface);
		if BAD_REF(CPUSurface) {
			REPORT_TYPE(CPUSurface,L"CollisionShape_Create_HeightField_UsingCPUSurface",CPUSurface)
		} else if (pCPUSurface->Width <= 0 || pCPUSurface->Height <= 0){
			MQ->InsertMessage(L"CollisionShape_Create_HeightField_UsingCPUSurface: The CPU surface is empty and can therefor not be used as a heightmap.");
		} else if (UpDimension < 0 || UpDimension > 2){
			MQ->InsertMessage(L"CollisionShape_Create_HeightField_UsingCPUSurface: UpDimension is not from 0 to 2 for X, Y or Z.");
		} else if (ChannelIndex < 0 || ChannelIndex > 3){
			MQ->InsertMessage(L"CollisionShape_Create_HeightField_UsingCPUSurface: ChannelIndex is not from 0 to 3 for Red, Green, Blue or Alpha.");
		} else if (HalfBoundHeight < 0.0f) {
			MQ->InsertMessage(L"CollisionShape_Create_HeightField_UsingCPUSurface: HalfBoundHeight is negative and can therefor not crate a valid bounding box from -HalfBoundHeight to HalfBoundHeight along the up dimension.");
		} else {
			Result = DGE.IDFromPointer(DGE.CollisionShape_Create_HeightField_UsingCPUSurface(pCPUSurface->Width,pCPUSurface->Height,pCPUSurface->CPUContent,-HalfBoundHeight,HalfBoundHeight,UpDimension,ChannelIndex,FlipQuadEdges,DiamondPattern,pCPUSurface));
		}
	)
	return Result;
}

// If 2 vertices are within the treshold from each other, the last one will be removed.
// If UseVertexSelection is selected then only vertices with more than 0.5 as selection value will be used to make the convex hull.
int CDFPGECtrl::CollisionShape_Create_ConvexHull_FromModel(int Model,int DetailLevel,int Quality,float WeldingTreshold,bool UseVertexSelection) {
	int Result;
	START_OF_CRITICAL_SECTION
		Result = 0;
		GET_FROM_REF(Model,Model);
		if BAD_REF(Model) {
			REPORT_TYPE(Model,L"CollisionShape_Create_ConvexHull_FromModel",Model)
		} else if (DetailLevel < 0 || DetailLevel > 2) {
			MQ->InsertMessage(L"CollisionShape_Create_ConvexHull_FromModel: DetailLevel is not a valid detail level. Use 0 to 2 for low to high detail level.");
		} else if (WeldingTreshold <= 0.0001f) {
			MQ->InsertMessage(L"CollisionShape_Create_ConvexHull_FromModel: WeldingTreshold must be greater than 0.0001.");
		} else if (Quality < 1 || Quality > 32) {
			MQ->InsertMessage(L"CollisionShape_Create_ConvexHull_FromModel: Quality must be an integer from 1 to 32 because less than 1 would not create anything and more than 32 would make a worse result at a higher cost.");
		} else {
			// Make the struct in the graphics engine
			CollisionShape_Struct* NewShape = DGE.CollisionShape_Create_ConvexHull(NULL);
			
			// Get it's convex hull shape and cast it since we know what it is
			btConvexHullShape* ConvexShape = (btConvexHullShape*)(NewShape->CollisionShape);
			
			// Fill convex hull with every vertice in the model that is not already added
			int Part;
			int Tri;
			int Vert;
			int Point;
			int MinDetail;
			int MaxDetail;
			bool OverlapExisting;
			LoopForwardLengthFromZero(Part,pModel->Model.GetNumberOfParts()) {
				MinDetail = pModel->Model.GetPartMinDetailLevel(Part);
				MaxDetail = pModel->Model.GetPartMaxDetailLevel(Part);
				if (DetailLevel >= MinDetail && DetailLevel <= MaxDetail) {
					LoopForwardLengthFromZero(Tri,pModel->Model.GetTriangleCountFromPart(Part)) {
						LoopForwardLengthFromZero(Vert,3) {
							// Either take everything by not using vertex selection or only take selected vertices
							if (!UseVertexSelection || pModel->Model.Vertice_GetSelected(Part,Tri,Vert) > 0.5f) {
								DVector3 VertPos;
								btVector3 PointPos;
								VertPos = pModel->Model.Vertice_GetPos(Part,Tri,Vert);
								
								//See if it is too close to any previous point
								OverlapExisting = false;
								
								LoopForwardLengthFromZero(Point,ConvexShape->getNumPoints()) {
									PointPos = ConvexShape->getUnscaledPoints()[Point];
									if (DistVec3(BulletToDX_V3(&PointPos),VertPos) < WeldingTreshold) {
										OverlapExisting = true;
									}
								}
								
								// If not then include it to the convex hull
								if (!OverlapExisting) {
									ConvexShape->addPoint(btVector3(VertPos.x,VertPos.y,VertPos.z));
								}
							}
						}
					}
				}
			}
			
			// Optimize
			btConvexHullShape* OptimizedConvexShape = OptimizeConvexShape(ConvexShape,Quality,0.0001f);
			if (OptimizedConvexShape) {
				// Use the new shape and delete the old shape
				NewShape->CollisionShape = OptimizedConvexShape;
				SAFE_DELETE(ConvexShape)
			} else {
				// Keep the old shape because it was not worth to optimize
				NewShape->CollisionShape = ConvexShape;
			}
			
			// Return the shape's ID
			Result = DGE.IDFromPointer(NewShape);
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

void CDFPGECtrl::CollisionShape_ConvexHull_Optimize(int ConvexHullShape, int Quality, float WeldingTreshold) {
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(CollisionShape,ConvexHullShape)
		if BAD_REF(ConvexHullShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_ConvexHull_Optimize",ConvexHullShape)
		} else if (pConvexHullShape->ShapeType != ShapeType_ConvexHull) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_Optimize: ConvexHullShape is not a convex hull collision shape.");
		} else if (WeldingTreshold <= 0.0001f) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_Optimize: WeldingTreshold must be greater than 0.0001.");
		} else if (Quality < 1 || Quality > 32) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_Optimize: Quality must be an integer from 1 to 32 because less than 1 would not create anything and more than 32 would make a worse result at a higher cost.");
		} else {
			btConvexHullShape* OldShape = (btConvexHullShape*)(pConvexHullShape->CollisionShape);
			
			// Weld
			btConvexHullShape* WeldedShape = WeldConvexShape(OldShape,WeldingTreshold);
			if (WeldedShape) {
				pConvexHullShape->CollisionShape = WeldedShape;
				SAFE_DELETE(OldShape)
				
				// Optimize
				btConvexHullShape* OptimizedShape = OptimizeConvexShape(WeldedShape,8,0.0001f);
				if (OptimizedShape) {
					// Replace and delete the old convex hull shape
					pConvexHullShape->CollisionShape = OptimizedShape;
					SAFE_DELETE(WeldedShape)
				}
			}
			DGE.Physics_CollisionShapeHasChanged(pConvexHullShape);
		}
	END_OF_CRITICAL_SECTION
}

btConvexHullShape* CDFPGECtrl::WeldConvexShape(btConvexHullShape* ConvexShape, float WeldingTreshold) {
	// Create an empty convex hull to fill with points
	btConvexHullShape* NewConvexShape = new btConvexHullShape();
	
	// Abort if we have no memory
	if (!NewConvexShape) {
		return NULL;
	}
	
	// Add points from the old convex hull
	int InputPoint;
	LoopForwardLengthFromZero(InputPoint,ConvexShape->getNumPoints()) {
		// If nothing set this flag to true then it will be false by default
		bool OverlapExisting = false;
		
		// Get the position of the input point
		btVector3 InputPos = ConvexShape->getUnscaledPoints()[InputPoint];
		
		// Check if a previous point is within the welding treshold
		int OutputPoint;
		LoopForwardLengthFromZero(OutputPoint,NewConvexShape->getNumPoints()) {
			
			// Get the position of the output point
			btVector3 OutputPos = NewConvexShape->getUnscaledPoints()[OutputPoint];
			
			// Check if the points are too close
			if (InputPos.distance(OutputPos) < WeldingTreshold) {
				
				// This point will not be added because it is too close to a previous point
				OverlapExisting = true;
			}
		}
		
		// Add the point if no previous point is within the welding treshold
		if (!OverlapExisting) {
			NewConvexShape->addPoint(InputPos);
		}
	}
	
	// Return a new shape with the remaining points
	return NewConvexShape;
}

// Treshold tells how close any other point may be to a point furthest away on one side of the plane.
// A lower quality gives fewer points but a higher quality will be a better approximation.
//     At least 4 and at most 20 is recomended for Quality.
// Treshold tells how far out a point must be from the others to remain and should only be large enough to handle rounding errors.
//     Otherwise, a sphere of points would vanish.
// No redundant point will remain inside the convex hull but some points may be removed to decrease detail level.
// Precondition: No points on the convex hull are at almost the same location because this is faster to remove when loading vertices from a triangle model.
// PostCondition: Returns an optimized convex shape with less or equal anount of points. If ConvexShape has less than 4 points or an allocation failed then NULL is returned so that you may keep your old shape.
// Worst case complexity: O(N*Q²) where N is the number of points in ConvexShape and Q is Quality.
btConvexHullShape* CDFPGECtrl::OptimizeConvexShape(btConvexHullShape* ConvexShape, int Quality, float Treshold) {
	int Side;
	int U;
	int V;
	int Point;
	int Smallest;
	int SecondSmallest;
	int SecondLargest;
	int Largest;
	float SmallestValue;
	float SecondSmallestValue;
	float SecondLargestValue;
	float LargestValue;
	btVector3 PointPos;
	bool* SelectedPoints;
	
	// Get the number of points
	int NumberOfPoints = ConvexShape->getNumPoints();
	
	// Reject trivial cases that can't even make a volume
	if (NumberOfPoints < 4) {
		// Invalid convex hull
		// This shaped should not be used
		return NULL;
	} else {
		// Allocate the array that will store our selection
		SelectedPoints = new bool[NumberOfPoints];
		
		// Abort if we have no memory
		if (!SelectedPoints) {
			return NULL;
		}
		
		// Initialize values
		LoopForwardLengthFromZero(Point,NumberOfPoints) {
			SelectedPoints[Point] = false;
		}
		
		// Test against each plane
		LoopForwardLengthFromZero(Side,3) {
			LoopForwardLengthFromZero(U,Quality) {
				LoopForwardLengthFromZero(V,Quality) {
					// Generate a normal by subdividing 3 sides of a cube
					// Since we use both sides, negative sides are not needed
					btVector3 Normal;
					switch (Side) {
					case 0:
						Normal = btVector3(1.0f,((((float)U + 0.5f) / Quality) * 2.0f) - 1.0f,((((float)V + 0.5f) / Quality) * 2.0f) - 1.0f);
					break;
					case 1:
						Normal = btVector3(((((float)U + 0.5f) / Quality) * 2.0f) - 1.0f,1.0f,((((float)V + 0.5f) / Quality) * 2.0f) - 1.0f);
					break;
					default:
						Normal = btVector3(((((float)U + 0.5f) / Quality) * 2.0f) - 1.0f,((((float)V + 0.5f) / Quality) * 2.0f) - 1.0f,1.0f);
					}
					
					// Normalize our direction for deterministic use of treshold
					Normal = Normal.normalize();
					
					// Reset minimum and maximum
					Smallest = -1;
					SecondSmallest = -1;
					SecondLargest = -1;
					Largest = -1;
					SmallestValue = 0.0f;
					SecondSmallestValue = 0.0f;
					SecondLargestValue = 0.0f;
					LargestValue = 0.0f;
					
					// Find the minimum and maximum dot products
					LoopForwardLengthFromZero(Point,NumberOfPoints) {
						// Get the position of the point
						PointPos = ConvexShape->getUnscaledPoints()[Point];
						
						// Take the dot product with the normal to see if any points are outside of every other point
						btScalar NewValue = PointPos.dot(Normal);
						
						// Remember the largest dot products
						if (Smallest == -1 || NewValue < SmallestValue) {
							// The previous smallest is the new second smallest
							SecondSmallest = Smallest;
							SecondSmallestValue = SmallestValue;
							
							// Store the new smallest
							Smallest = Point;
							SmallestValue = NewValue;
						} else if (SecondSmallest == -1 || NewValue < SecondSmallestValue) {
							// Store the new second smallest
							SecondSmallest = Point;
							SecondSmallestValue = NewValue;
						}
						
						// Remember the smallest dot products
						if (Largest == -1 || NewValue > LargestValue) {
							// The previous largest is the new second largest
							SecondLargest = Largest;
							SecondLargestValue = LargestValue;
							
							// Store the new largest
							Largest = Point;
							LargestValue = NewValue;
						} else if (SecondLargest == -1 || NewValue > SecondLargestValue) {
							// Store the new second largest
							SecondLargest = Point;
							SecondLargestValue = NewValue;
						}
					}
					
					// Any outstanding endpoints along the normal will be selected to keep in the new shape
					if (LargestValue > SecondLargestValue + Treshold) {
						SelectedPoints[Largest] = true;
					}
					if (SmallestValue < SecondSmallestValue - Treshold) {
						SelectedPoints[Smallest] = true;
					}
				}
			}
		}
		
		// Count how many points we made
		int NewNumberOfPoints = 0;
		LoopForwardLengthFromZero(Point,NumberOfPoints) {
			if (SelectedPoints[Point]) {
				NewNumberOfPoints++;
			}
		}
		
		// Abort if we removed too much from the shape
		if (NewNumberOfPoints < 4) {
			delete[](SelectedPoints);
			return NULL;
		}
		
		// Create an empty convex hull to fill with points
		btConvexHullShape* NewConvexShape = new btConvexHullShape();
		
		// Abort if we have no memory
		if (!NewConvexShape) {
			delete[](SelectedPoints);
			return NULL;
		}
		
		// Add all selected points from the old convex hull
		// This would be faster if btConvexHullShape could preallocate memory of the final size before getting the points
		LoopForwardLengthFromZero(Point,NumberOfPoints) {
			if (SelectedPoints[Point]) {
				NewConvexShape->addPoint(ConvexShape->getUnscaledPoints()[Point]);
			}
		}
		
		// Remove our selection array
		delete[](SelectedPoints);
		
		// Return a new shape with the selected points
		return NewConvexShape;
	}
}

int CDFPGECtrl::CollisionShape_ConvexHull_MakeCopy(int OldConvexHullShape) {
	int Result;
	START_OF_CRITICAL_SECTION
		Result = 0;
		GET_FROM_REF(CollisionShape,OldConvexHullShape);
		if BAD_REF(OldConvexHullShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_ConvexHull_MakeCopy",OldConvexHullShape)
		} else if (pOldConvexHullShape->ShapeType != ShapeType_ConvexHull) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_MakeCopy: OldConvexHullShape is not a convex hull collision shape.");
		} else {
			CollisionShape_Struct* NewShape;
			NewShape = DGE.CollisionShape_Create_ConvexHull(NULL);
			btConvexHullShape* NewConvexShape;
			NewConvexShape = (btConvexHullShape*)(NewShape->CollisionShape);
			btConvexHullShape* OldConvexShape = (btConvexHullShape*)(pOldConvexHullShape->CollisionShape);
			int Vert;
			LoopForwardLengthFromZero(Vert,OldConvexShape->getNumPoints()) {
				NewConvexShape->addPoint(OldConvexShape->getUnscaledPoints()[Vert]);
			}
			NewConvexShape->setLocalScaling(OldConvexShape->getLocalScaling());
			Result = DGE.IDFromPointer(NewShape);
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

void CDFPGECtrl::CollisionShape_SetLocalScaling(int CollisionShape, float X, float Y, float Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_SetLocalScaling",CollisionShape)
		} else {
			pCollisionShape->CollisionShape->setLocalScaling(btVector3(X,Y,Z));
			pCollisionShape->HasValidLocalInertiaPerMass = false;
			DGE.Physics_CollisionShapeHasChanged(pCollisionShape);
		}
	)
}

void CDFPGECtrl::CollisionShape_GetLocalScaling_OutV3(int CollisionShape) {
	CRITICAL_CODE_SECTION(
		ClearMatrixBuffer();
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetLocalScaling_OutV3",CollisionShape)
		} else {
			btVector3 S = pCollisionShape->CollisionShape->getLocalScaling();
			X1 = S.getX();
			Y1 = S.getY();
			Z1 = S.getZ();
		}
	)
}

void CDFPGECtrl::CollisionShape_Compound_AddChild(int CompoundShape, int ChildShape,float Pos_X, float Pos_Y, float Pos_Z, float XAxis_X, float XAxis_Y, float XAxis_Z, float YAxis_X, float YAxis_Y, float YAxis_Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(CollisionShape,CompoundShape)
		GET_FROM_REF(CollisionShape,ChildShape)
		if BAD_REF(CompoundShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_Compound_AddChild",CompoundShape)
		} else if BAD_REF(ChildShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_Compound_AddChild",ChildShape)
		} else if (!(pCompoundShape->CollisionShape->isCompound())) {
			MQ->InsertMessage(L"CollisionShape_Compound_AddChild: CompoundShape is not a compound collision shape.");
		} else if (pChildShape->CollisionShape->isCompound()) {
			MQ->InsertMessage(L"CollisionShape_Compound_AddChild: ChildShape is also a compound collision shape. Compound shapes can not contain other compound shapes because that would make it hard for non recursive languages to get all children.");
		} else if (pChildShape->CollisionShape->isNonMoving()) {
			MQ->InsertMessage(L"CollisionShape_Compound_AddChild: Compound shapes may not contain static shapes.");
		} else if (pChildShape->CollisionShape->isInfinite()) {
			MQ->InsertMessage(L"CollisionShape_Compound_AddChild: Compound shapes may not contain infinite shapes.");
		} else if (pChildShape->HasCompoundParent) {
			MQ->InsertMessage(L"CollisionShape_Compound_AddChild: Because of a limitation in the physics engine, a shape can not be part of more than one compound shape.");
		} else {
			pCompoundShape->HasValidLocalInertiaPerMass = false;
			pChildShape->HasCompoundParent = true;
			StartUsing(pChildShape->useCount);
			btTransform NewTrans; MakeOrthogonalBtTransform(&NewTrans,DVector3(Pos_X,Pos_Y,Pos_Z),DVector3(XAxis_X,XAxis_Y,XAxis_Z),DVector3(YAxis_X,YAxis_Y,YAxis_Z));
			((ModifiedCompound*)(pCompoundShape->CollisionShape))->addChildShape(NewTrans,pChildShape->CollisionShape);
			DGE.Physics_CollisionShapeHasChanged(pCompoundShape);
		}
	)
}

void CDFPGECtrl::CollisionShape_ConvexHull_AddPoint(int ConvexHullShape, float X, float Y, float Z) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(CollisionShape,ConvexHullShape)
		if BAD_REF(ConvexHullShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_ConvexHull_AddPoint",ConvexHullShape)
		} else if (pConvexHullShape->ShapeType != ShapeType_ConvexHull) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_AddPoint: ConvexHullShape is not a convex hull collision shape.");
		} else {
			((btConvexHullShape*)(pConvexHullShape->CollisionShape))->addPoint(btVector3(X,Y,Z));
			pConvexHullShape->HasValidLocalInertiaPerMass = false;
			DGE.Physics_CollisionShapeHasChanged(pConvexHullShape);
		}
	)
}

int CDFPGECtrl::CollisionShape_ConvexHull_GetNumberOfPoints(int ConvexHullShape) {
	int Result;
	START_OF_CRITICAL_SECTION
		Result = -1;
		GET_FROM_REF(CollisionShape,ConvexHullShape)
		if BAD_REF(ConvexHullShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_ConvexHull_GetNumberOfPoints",ConvexHullShape)
		} else if (pConvexHullShape->ShapeType != ShapeType_ConvexHull) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_GetNumberOfPoints: ConvexHullShape is not a convex hull collision shape.");
		} else {
			Result = ((btConvexHullShape*)(pConvexHullShape->CollisionShape))->getNumPoints();
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

void CDFPGECtrl::CollisionShape_ConvexHull_GetPoint_OutV3(int ConvexHullShape, int Index) {
	START_OF_CRITICAL_SECTION
		ClearMatrixBuffer();
		GET_FROM_REF(CollisionShape,ConvexHullShape)
		if BAD_REF(ConvexHullShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_ConvexHull_GetPoint_OutV3",ConvexHullShape)
		} else if (pConvexHullShape->ShapeType != ShapeType_ConvexHull) {
			MQ->InsertMessage(L"CollisionShape_ConvexHull_GetPoint_OutV3: ConvexHullShape is not a convex hull collision shape.");
		} else if (Index < 0 || Index >= ((btConvexHullShape*)(pConvexHullShape->CollisionShape))->getNumPoints()){
			MQ->InsertMessage(L"CollisionShape_ConvexHull_GetPoint_OutV3: Index is out of bound. Use 0 to NumberOfPoints - 1.");
		} else {
			btConvexHullShape* ConvexHullShape = (btConvexHullShape*)(pConvexHullShape->CollisionShape);
			btVector3 Point = ConvexHullShape->getUnscaledPoints()[Index];
			SetBulletVector3ToMB(&Point);
		}
	END_OF_CRITICAL_SECTION
}

void CDFPGECtrl::CollisionShape_GetLocalInertiaPerMass_OutV3(int CollisionShape) {
	START_OF_CRITICAL_SECTION
		ClearMatrixBuffer();
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetLocalInertiaPerMass_OutV3",CollisionShape)
		} else {
			// Calculate the shape's local inertia if needed
			if (!(pCollisionShape->HasValidLocalInertiaPerMass)) {
				pCollisionShape->CollisionShape->calculateLocalInertia(1.0f,pCollisionShape->LocalInertiaPerMass);
				pCollisionShape->HasValidLocalInertiaPerMass = true;
			}
			
			// Return it
			SetBulletVector3ToMB(&(pCollisionShape->LocalInertiaPerMass));
		}
	END_OF_CRITICAL_SECTION
}

void CDFPGECtrl::CollisionShape_Delete(int CollisionShape) {
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_Delete",CollisionShape)
		} else {
			DGE.Delete_CollisionShape(pCollisionShape);
		}
	)
}

float CDFPGECtrl::CollisionShape_GetRadius(int CollisionShape) {
	float Result;
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetRadius",CollisionShape)
		} else {
			switch (pCollisionShape->ShapeType) {
			case ShapeType_Sphere:
			case ShapeType_Capsule:
				Result = ((btConvexInternalShape*)(pCollisionShape->CollisionShape))->getImplicitShapeDimensions().getX();
			break;
			default:
				MQ->InsertMessage(L"CollisionShape_GetRadius: CollisionShape is not a sphere or capsule.");
				Result = -1.0f;
			}
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

float CDFPGECtrl::CollisionShape_GetHalfWidth(int CollisionShape) {
	float Result;
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetHalfWidth",CollisionShape)
		} else {
			switch (pCollisionShape->ShapeType) {
			case ShapeType_Box:
			case ShapeType_Cone:
			case ShapeType_Cylinder:
				Result = ((btConvexInternalShape*)(pCollisionShape->CollisionShape))->getImplicitShapeDimensions().getX();
			break;
			default:
				MQ->InsertMessage(L"CollisionShape_GetHalfWidth: CollisionShape is not a box, cone or cylinder.");
				Result = -1.0f;
			}
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

float CDFPGECtrl::CollisionShape_GetHalfHeight(int CollisionShape) {
	float Result;
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetHalfHeight",CollisionShape)
		} else {
			switch (pCollisionShape->ShapeType) {
			case ShapeType_Box:
			case ShapeType_Cone:
			case ShapeType_Cylinder:
			case ShapeType_Capsule:
				Result = ((btConvexInternalShape*)(pCollisionShape->CollisionShape))->getImplicitShapeDimensions().getY();
			break;
			default:
				MQ->InsertMessage(L"CollisionShape_GetHalfHeight: CollisionShape is not a box, cone, cylinder or capsule.");
				Result = -1.0f;
			}
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

float CDFPGECtrl::CollisionShape_GetHalfDepth(int CollisionShape) {
	float Result;
	START_OF_CRITICAL_SECTION
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetHalfDepth",CollisionShape)
		} else {
			switch (pCollisionShape->ShapeType) {
			case ShapeType_Box:
			case ShapeType_Cone:
			case ShapeType_Cylinder:
				Result = ((btConvexInternalShape*)(pCollisionShape->CollisionShape))->getImplicitShapeDimensions().getZ();
			break;
			default:
				MQ->InsertMessage(L"CollisionShape_GetHalfDepth: CollisionShape is not a box, cone or cylinder.");
				Result = -1.0f;
			}
		}
	END_OF_CRITICAL_SECTION
	return Result;
}

int CDFPGECtrl::CollisionShape_GetNumberOfChildren(int CollisionShape) {
	int Result;
	CRITICAL_CODE_SECTION(
		GET_FROM_REF(CollisionShape,CollisionShape)
		if BAD_REF(CollisionShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_GetNumberOfChildren",CollisionShape)
			Result = -2;
		} else {
			if (pCollisionShape->CollisionShape->isCompound()) {
				Result = ((ModifiedCompound*)(pCollisionShape->CollisionShape))->getNumChildShapes();
			} else {
				Result = -1;
			}
		}
	)
	return Result;
}

int CDFPGECtrl::CollisionShape_Compound_GetChild_OutM4(int CompoundShape,int Index) {
	int ChildID;
	START_OF_CRITICAL_SECTION
		ClearMatrixBuffer();
		GET_FROM_REF(CollisionShape,CompoundShape)
		if BAD_REF(CompoundShape) {
			REPORT_TYPE(CollisionShape,L"CollisionShape_Compound_GetChild_OutM4",CompoundShape)
		} else if (!(pCompoundShape->CollisionShape->isCompound())) {
			MQ->InsertMessage(L"CollisionShape_Compound_GetChild_OutM4: CompoundShape is not a compound collision shape.");
			ChildID = -1;
		} else {
			int NumberOfChildren = ((ModifiedCompound*)(pCompoundShape->CollisionShape))->getNumChildShapes();
			if (Index < 0 || Index >= NumberOfChildren) {
				MQ->InsertMessage(L"CollisionShape_Compound_GetChild_OutM4: Index is out of bound. Index must be from 0 to the number of children - 1.");
			} else {
				btCollisionShape* pChild = ((ModifiedCompound*)(pCompoundShape->CollisionShape))->getChildShape(Index);
				if (pChild) {
					// Get the child
					CollisionShape_Struct* pChildStruct = (CollisionShape_Struct*)(pChild->getUserPointer());
					
					// Get the child ID
					ChildID = pChildStruct->ID;
					
					// Get the transformation
					btTransform Trans = ((ModifiedCompound*)(pCompoundShape->CollisionShape))->getChildTransform(Index);
					SetBulletTransformToMB(&Trans);
				} else {
					MQ->InsertMessage(L"CollisionShape_Compound_GetChild_OutM4: Unexpected error when getting the child shape's pointer from the Bullet physics engine. The pointer was null.");
					ChildID = -1;
				}
			}
		}
	END_OF_CRITICAL_SECTION
	return ChildID;
}
