/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 * Copyright (C) 2006-2010 - Frictional Games
 *
 * This file is part of HPL1 Engine.
 */

#include "hpl1/engine/math/Math.h"
#include "hpl1/engine/system/low_level_system.h"
#include "hpl1/hpl1.h"

namespace hpl {

static char mpTempChar[1024];

//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------

int cMath::RandRectl(int alMin, int alMax) {
	return Hpl1::g_engine->getRandomNumber(alMax - alMin) + alMin;
}

//-----------------------------------------------------------------------

float cMath::RandRectf(float afMin, float afMax) {
	float fRand = static_cast<float>(Hpl1::g_engine->getRandomNumber(UINT_MAX)) / static_cast<float>(UINT_MAX);

	return afMin + fRand * (afMax - afMin);
}

//-----------------------------------------------------------------------

cVector2f cMath::RandRectVector2f(const cVector3f &avMin, const cVector3f &avMax) {
	return cVector2f(RandRectf(avMin.x, avMax.x),
					 RandRectf(avMin.y, avMax.y));
}

//-----------------------------------------------------------------------

cVector3f cMath::RandRectVector3f(const cVector3f &avMin, const cVector3f &avMax) {
	return cVector3f(RandRectf(avMin.x, avMax.x),
					 RandRectf(avMin.y, avMax.y),
					 RandRectf(avMin.z, avMax.z));
}

//-----------------------------------------------------------------------

cColor cMath::RandRectColor(const cColor &aMin, const cColor &aMax) {
	return cColor(RandRectf(aMin.r, aMax.r),
				  RandRectf(aMin.g, aMax.g),
				  RandRectf(aMin.b, aMax.b),
				  RandRectf(aMin.a, aMax.a));
}

//-----------------------------------------------------------------------

void cMath::Randomize(int alSeed) {
#if 0
	if (alSeed == -1) {
		srand((unsigned)time(NULL));
	} else {
		srand(alSeed);
	}
#endif
}
//-----------------------------------------------------------------------

float cMath::Dist2D(const cVector2f &avPosA, const cVector2f &avPosB) {
	float fDx = avPosA.x - avPosB.x;
	float fDy = avPosA.y - avPosB.y;

	return sqrt(fDx * fDx + fDy * fDy);
}

//-----------------------------------------------------------------------

float cMath::Dist2D(const cVector3f &avPosA, const cVector3f &avPosB) {
	float fDx = avPosA.x - avPosB.x;
	float fDy = avPosA.y - avPosB.y;

	return sqrt(fDx * fDx + fDy * fDy);
}

//-----------------------------------------------------------------------

float cMath::SqrDist2D(const cVector2f &avPosA, const cVector2f &avPosB) {
	float fDx = avPosA.x - avPosB.x;
	float fDy = avPosA.y - avPosB.y;

	return fDx * fDx + fDy * fDy;
}

//-----------------------------------------------------------------------

float cMath::SqrDist2D(const cVector3f &avPosA, const cVector3f &avPosB) {
	float fDx = avPosA.x - avPosB.x;
	float fDy = avPosA.y - avPosB.y;

	return fDx * fDx + fDy * fDy;
}

//-----------------------------------------------------------------------

bool cMath::BoxCollision(cRect2l aRect1, cRect2l aRect2) {
	return (aRect1.x > aRect2.x + (aRect2.w - 1) || aRect2.x > aRect1.x + (aRect1.w - 1) ||
			aRect1.y > aRect2.y + (aRect2.h - 1) || aRect2.y > aRect1.y + (aRect1.h - 1)) == false;
}

//-----------------------------------------------------------------------

bool cMath::BoxCollision(cRect2f aRect1, cRect2f aRect2) {
	return (aRect1.x > aRect2.x + (aRect2.w) || aRect2.x > aRect1.x + (aRect1.w) ||
			aRect1.y > aRect2.y + (aRect2.h) || aRect2.y > aRect1.y + (aRect1.h)) == false;
}

//-----------------------------------------------------------------------

bool cMath::PointBoxCollision(cVector2f avPoint, cRect2f aRect) {
	if (avPoint.x < aRect.x || avPoint.x > aRect.x + aRect.w || avPoint.y < aRect.y || avPoint.y > aRect.y + aRect.h)
		return false;
	else
		return true;
}

//-----------------------------------------------------------------------

bool cMath::BoxFit(cRect2l aRectSrc, cRect2l aRectDest) {
	// check is size is smaller and doesn't overlap
	if (aRectSrc.w > aRectDest.w || aRectSrc.h > aRectDest.h ||
		aRectSrc.x + aRectSrc.w > aRectDest.x + aRectDest.w ||
		aRectSrc.y + aRectSrc.h > aRectDest.y + aRectDest.h)
		return false;

	// check if x,y is in borders
	if (aRectSrc.x < aRectDest.x || aRectSrc.y < aRectDest.y ||
		aRectSrc.x > aRectDest.x + aRectDest.w || aRectSrc.y > aRectDest.y + aRectDest.h)
		return false;

	return true;
}

//-----------------------------------------------------------------------

bool cMath::BoxFit(cRect2f aRectSrc, cRect2f aRectDest) {
	// check is size is smaller and doesn't overlap
	if (aRectSrc.w > aRectDest.w || aRectSrc.h > aRectDest.h ||
		aRectSrc.x + aRectSrc.w > aRectDest.x + aRectDest.w ||
		aRectSrc.y + aRectSrc.h > aRectDest.y + aRectDest.h)
		return false;

	// check if x,y is in borders
	if (aRectSrc.x < aRectDest.x || aRectSrc.y < aRectDest.y ||
		aRectSrc.x > aRectDest.x + aRectDest.w || aRectSrc.y > aRectDest.y + aRectDest.h)
		return false;

	return true;
}

//-----------------------------------------------------------------------

cRect2f &cMath::ClipRect(cRect2f &aRectSrc, const cRect2f &aRectDest) {
	if (aRectSrc.x < aRectDest.x) {
		aRectSrc.w -= aRectDest.x - aRectSrc.x;
		aRectSrc.x = aRectDest.x;
	}
	if (aRectSrc.y < aRectDest.y) {
		aRectSrc.h -= aRectDest.y - aRectSrc.y;
		aRectSrc.y = aRectDest.y;
	}

	if (aRectSrc.x + aRectSrc.w > aRectDest.x + aRectDest.w) {
		aRectSrc.w -= (aRectSrc.x + aRectSrc.w) - (aRectDest.x + aRectDest.w);
	}
	if (aRectSrc.y + aRectSrc.h > aRectDest.y + aRectDest.h) {
		aRectSrc.h -= (aRectSrc.y + aRectSrc.h) - (aRectDest.y + aRectDest.h);
	}

	return aRectSrc;
}

//-----------------------------------------------------------------------

bool cMath::CheckCollisionBV(cBoundingVolume &aBV1, cBoundingVolume &aBV2) {
	//////////////////////////////////////////
	// Check Sphere collision.
	// Not needed I think.
	/*float fRadiusSum = aBV1->GetRadius() + aBV2->GetRadius();
	cVector3f vSepAxis = aBV1->GetWorldCenter() - aBV2->GetWorldCenter();
	if(vSepAxis.SqrLength() > (fRadiusSum * fRadiusSum))
	{
		return eBVCollision_Outside;
	}*/

	///////////////////////////////////////////
	// Check box collision
	aBV1.UpdateSize();
	aBV2.UpdateSize();

	const cVector3f &vMin1 = aBV1.mvWorldMin;
	const cVector3f &vMin2 = aBV2.mvWorldMin;

	const cVector3f &vMax1 = aBV1.mvWorldMax;
	const cVector3f &vMax2 = aBV2.mvWorldMax;

	// Log("Min1: %s Max1: %s  vs  Min2: %s Max2: %s\n", vMin1.ToString().c_str(),vMax1.ToString().c_str(),
	//												vMin2.ToString().c_str(), vMax2.ToString().c_str());

	if (vMax1.x < vMin2.x || vMax1.y < vMin2.y || vMax1.z < vMin2.z ||
		vMax2.x < vMin1.x || vMax2.y < vMin1.y || vMax2.z < vMin1.z) {
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------

bool cMath::PointBVCollision(const cVector3f &avPoint, cBoundingVolume &aBV) {
	cVector3f vMax = aBV.GetMax();
	cVector3f vMin = aBV.GetMin();

	if (avPoint.x > vMax.x || avPoint.y > vMax.y || avPoint.z > vMax.z ||
		avPoint.x < vMin.x || avPoint.y < vMin.y || avPoint.z < vMin.z) {
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------

bool cMath::GetClipRectFromBV(cRect2l &aDestRect, cBoundingVolume &aBV,
							  const cMatrixf &a_mtxView, const cMatrixf &a_mtxProj,
							  float afNearClipPlane, const cVector2l &avScreenSize) {
	cVector3f vMax = aBV.GetMax();
	cVector3f vMin = aBV.GetMin();
	cVector3f vCorners[8];
	vCorners[0] = cVector3f(vMax.x, vMax.y, vMax.z);
	vCorners[1] = cVector3f(vMax.x, vMax.y, vMin.z);
	vCorners[2] = cVector3f(vMax.x, vMin.y, vMax.z);
	vCorners[3] = cVector3f(vMax.x, vMin.y, vMin.z);

	vCorners[4] = cVector3f(vMin.x, vMax.y, vMax.z);
	vCorners[5] = cVector3f(vMin.x, vMax.y, vMin.z);
	vCorners[6] = cVector3f(vMin.x, vMin.y, vMax.z);
	vCorners[7] = cVector3f(vMin.x, vMin.y, vMin.z);

	bool bVisible = true;

	float fMaxZ = -afNearClipPlane;
	for (int i = 0; i < 8; i++) {
		vCorners[i] = MatrixMul(a_mtxView, vCorners[i]);

		if (vCorners[i].z > fMaxZ) {
			vCorners[i].z = 0;
			bVisible = false; // we are inside the light
		}

		vCorners[i] = MatrixMulDivideW(a_mtxProj, vCorners[i]);
	}

	if (bVisible == false)
		return false;

	vMax = vCorners[0];
	vMin = vCorners[0];
	for (int i = 1; i < 8; i++) {
		if (vCorners[i].x < vMin.x)
			vMin.x = vCorners[i].x;
		if (vCorners[i].x > vMax.x)
			vMax.x = vCorners[i].x;

		// Y
		if (vCorners[i].y < vMin.y)
			vMin.y = vCorners[i].y;
		if (vCorners[i].y > vMax.y)
			vMax.y = vCorners[i].y;
	}

	// Clip min and max
	if (vMin.x < -1)
		vMin.x = -1;
	if (vMin.y < -1)
		vMin.y = -1;
	if (vMax.x > 1)
		vMax.x = 1;
	if (vMax.y > 1)
		vMax.y = 1;

	// Get the screen coordinates
	cVector2f vHalfScreenSize = cVector2f((float)avScreenSize.x, (float)avScreenSize.y) * 0.5f;
	cVector2l vTopLeft;
	cVector2l vBottomRight;
	vTopLeft.x = (int)(vHalfScreenSize.x + vMin.x * vHalfScreenSize.x);
	vTopLeft.y = (int)(vHalfScreenSize.y + vMax.y * -vHalfScreenSize.y);
	vBottomRight.x = (int)(vHalfScreenSize.x + vMax.x * vHalfScreenSize.x);
	vBottomRight.y = (int)(vHalfScreenSize.y + vMin.y * -vHalfScreenSize.y);

	// Clip the screen coordinates
	if (vTopLeft.x < 0)
		vTopLeft.x = 0;
	if (vTopLeft.y < 0)
		vTopLeft.y = 0;
	if (vBottomRight.x > avScreenSize.x - 1)
		vBottomRight.x = avScreenSize.x - 1;
	if (vBottomRight.y > avScreenSize.y - 1)
		vBottomRight.y = avScreenSize.y - 1;

	aDestRect.x = vTopLeft.x;
	aDestRect.y = vTopLeft.y;
	aDestRect.w = (vBottomRight.x - vTopLeft.x) + 1;
	aDestRect.h = (vBottomRight.y - vTopLeft.y) + 1;

	return true;
}

//-----------------------------------------------------------------------

bool cMath::CheckSphereInPlanes(const cVector3f &avCenter, float afRadius,
								const cPlanef *apPlanes, int alPlaneCount) {
	for (int i = 0; i < alPlaneCount; i++) {
		float fDist = cMath::PlaneToPointDist(apPlanes[i], avCenter);

		if (fDist < -afRadius) {
			return false;
		}
	}

	return true;
}

//-----------------------------------------------------------------------

float cMath::GetFraction(float afVal) {
	return afVal - floor(afVal);
}

//-----------------------------------------------------------------------

float cMath::Modulus(float afDividend, float afDivisor) {
	float fNum = floor(ABS(afDividend / afDivisor));

	float fRemain = ABS(afDividend) - ABS(afDivisor) * fNum;

	return fRemain;
}

//-----------------------------------------------------------------------

float cMath::Wrap(float afX, float afMin, float afMax) {
	// Quick check if value is okay. If so just return.
	if (afX >= afMin && afX <= afMax)
		return afX;

	// Change setup so that min is 0
	afMax = afMax - afMin;
	float fOffSet = afMin;
	afMin = 0;
	afX = afX - fOffSet;

	float fNumOfMax = floor(ABS(afX / afMax));

	if (afX >= afMax)
		afX = afX - fNumOfMax * afMax;
	else if (afX < afMin)
		afX = ((fNumOfMax + 1.0f) * afMax) + afX;

	return afX + fOffSet;
}

//-----------------------------------------------------------------------

float cMath::Clamp(float afX, float afMin, float afMax) {
	if (afX > afMax)
		afX = afMax;
	else if (afX < afMin)
		afX = afMin;

	return afX;
}

//-----------------------------------------------------------------------

float cMath::GetAngleDistanceRad(float afAngle1, float afAngle2) {
	return GetAngleDistance(afAngle1, afAngle2, k2Pif);
}

float cMath::GetAngleDistanceDeg(float afAngle1, float afAngle2) {
	return GetAngleDistance(afAngle1, afAngle2, 360.0f);
}

float cMath::GetAngleDistance(float afAngle1, float afAngle2, float afMaxAngle) {
	afAngle1 = Wrap(afAngle1, 0, afMaxAngle);
	afAngle2 = Wrap(afAngle2, 0, afMaxAngle);

	if (afAngle1 == afAngle2) {
		return 0;
	} else {
		float fDist1 = afAngle2 - afAngle1;
		float fDist2 = afMaxAngle - ABS(fDist1);

		if (fDist1 > 0)
			fDist2 = -fDist2;

		if (ABS(fDist1) < ABS(fDist2))
			return fDist1;
		else
			return fDist2;
	}
}

//-----------------------------------------------------------------------

float cMath::TurnAngle(float afAngle, float afFinalAngle, float afSpeed, float afMaxAngle) {
	if (afAngle != afFinalAngle) {
		float fAngleDist = afFinalAngle - afAngle;
		if ((afFinalAngle > afAngle && fAngleDist < afMaxAngle) ||
			(afFinalAngle < afAngle && fAngleDist < (-afMaxAngle)))
			afAngle = afAngle + afSpeed;
		else
			afAngle = afAngle + (-afSpeed);
	}
	if (Abs(GetAngleDistance(afAngle, afFinalAngle, afMaxAngle * 2)) <= (afSpeed * 1.5))
		afAngle = afFinalAngle;

	return afAngle;
}

float cMath::TurnAngleRad(float afAngle, float afFinalAngle, float afSpeed) {
	return TurnAngle(afAngle, afFinalAngle, afSpeed, kPif);
}

float cMath::TurnAngleDeg(float afAngle, float afFinalAngle, float afSpeed) {
	return TurnAngle(afAngle, afFinalAngle, afSpeed, 180.0f);
}

//-----------------------------------------------------------------------

float cMath::GetAngleFromPoints2D(const cVector2f &avStartPos, const cVector2f &avGoalPos) {
	float fDx;
	float fDy;
	float fAns = 0.0;

	fDx = avGoalPos.x - avStartPos.x;
	fDy = avGoalPos.y - avStartPos.y;
	if (fDx == 0)
		fDx = 0.00001f;
	if (fDy == 0)
		fDy = 0.00001f;

	if (fDx >= 0 && fDy < 0) {
		fAns = atan(fDx / (-fDy));
		// Log("1\n");
	} else if (fDx >= 0 && fDy >= 0) {
		fAns = atan(fDy / fDx) + kPi2f;
		// Log("2\n");
	} else if (fDx < 0 && fDy >= 0) {
		fAns = atan((-fDx) / fDy) + kPif;
		// Log("3\n");
	} else if (fDx < 0 && fDy < 0) {
		fAns = atan((-fDy) / (-fDx)) + kPi2f + kPif;
		// Log("4\n");
	}
	return fAns;
}

//-----------------------------------------------------------------------

cVector2f cMath::GetVectorFromAngle2D(float afAngle, float afLength) {
	cVector2f vVec;

	vVec.x = sin(afAngle) * afLength;
	vVec.y = -cos(afAngle) * afLength;

	return vVec;
}

//-----------------------------------------------------------------------

void cMath::GetAngleFromVector(const cVector2f &avVec, float *apAngle, float *apLength) {
	*apLength = sqrt(avVec.x * avVec.x + avVec.y * avVec.y);

	*apAngle = GetAngleFromPoints2D(0, avVec);
}

//-----------------------------------------------------------------------

cVector2f cMath::ProjectVector2D(const cVector2f &avSrcVec, const cVector2f &avDestVec) {
	float fTemp = (avSrcVec.x * avDestVec.x + avSrcVec.y * avDestVec.y) /
				  (avDestVec.x * avDestVec.x + avDestVec.y * avDestVec.y);

	return avDestVec * fTemp;
}

//-----------------------------------------------------------------------

float cMath::ToRad(float afAngle) {
	return (afAngle / 360.0f) * k2Pif;
}

//-----------------------------------------------------------------------

float cMath::ToDeg(float afAngle) {
	return (afAngle / k2Pif) * 360.0f;
}

//-----------------------------------------------------------------------

int cMath::Log2ToInt(int alX) {
	switch (alX) {
	case 1:
		return 0;
	case 2:
		return 1;
	case 4:
		return 2;
	case 8:
		return 3;
	case 16:
		return 4;
	case 32:
		return 5;
	case 64:
		return 6;
	case 128:
		return 7;
	case 256:
		return 8;
	case 512:
		return 9;
	default:
		return (int)floor((log((float)alX) / log(2.0f)) + 0.5f);
	}
	// return (int)floor((log((float)aX) / log(2.0f)) + 0.5f);
}

//-----------------------------------------------------------------------

bool cMath::IsPow2(int alX) {
	switch (alX) {
	case 0:
		return true;
	case 1:
		return true;
	case 2:
		return true;
	case 4:
		return true;
	case 8:
		return true;
	case 16:
		return true;
	case 32:
		return true;
	case 64:
		return true;
	case 128:
		return true;
	case 256:
		return true;
	case 512:
		return true;
	case 1024:
		return true;
	case 2048:
		return true;
	case 4096:
		return true;
	case 8192:
		return true;
	default:
		return false;
	}
}

//-----------------------------------------------------------------------

float cMath::InterpolateFloat(float afA, float afB, float afT) {
	return afA * (1 - afT) + afB * afT;
}

//-----------------------------------------------------------------------

cVector3f cMath::Vector3Cross(const cVector3f &avVecA, const cVector3f &avVecB) {
	cVector3f vResult;

	vResult.x = avVecA.y * avVecB.z - avVecA.z * avVecB.y;
	vResult.y = avVecA.z * avVecB.x - avVecA.x * avVecB.z;
	vResult.z = avVecA.x * avVecB.y - avVecA.y * avVecB.x;

	return vResult;
}

//-----------------------------------------------------------------------

float cMath::Vector3Dot(const cVector3f &avVecA, const cVector3f &avVecB) {
	return avVecA.x * avVecB.x + avVecA.y * avVecB.y + avVecA.z * avVecB.z;
}

//-----------------------------------------------------------------------

cVector3f cMath::ProjectVector3D(const cVector3f &avSrcVec, const cVector3f &avDestVec) {
	float fTemp = (avSrcVec.x * avDestVec.x + avSrcVec.y * avDestVec.y + avSrcVec.z * avDestVec.z) /
				  (avDestVec.x * avDestVec.x + avDestVec.y * avDestVec.y + avDestVec.z * avDestVec.z);

	return avDestVec * fTemp;
}

//-----------------------------------------------------------------------

float cMath::Vector3Angle(const cVector3f &avVecA, const cVector3f &avVecB) {
	float fCos = Vector3Dot(avVecA, avVecB);

	if (ABS(fCos - 1) <= kEpsilonf)
		return 0;

	return acos(fCos);
}

//-----------------------------------------------------------------------

cVector3f cMath::Vector3UnProject(const cVector3f &avVec, const cRect2f &aScreenRect,
								  cMatrixf a_mtxViewProj) {
	cMatrixf mtxInvViewProj = MatrixInverse(a_mtxViewProj);

	cVector3f vNormalized;
	vNormalized.x = ((avVec.x - aScreenRect.x) * 2.0f / aScreenRect.w) - 1.0f;
	vNormalized.y = -(((avVec.y - aScreenRect.y) * 2.0f / aScreenRect.h) - 1.0f);
	vNormalized.z = 2.0f * avVec.z - 1.0f;

	// Log("Normalized: %s\n",vNormalized.ToString().c_str());

	// Object coordinates.
	vNormalized = MatrixMulDivideW(mtxInvViewProj, vNormalized);
	// vNormalized = MatrixMul(mtxInvViewProj,vNormalized);

	// Log("Normalized After: %s\n",vNormalized.ToString().c_str());

	return vNormalized * -1;
}

//-----------------------------------------------------------------------

// Helper function for GetAnglesFromPoints3D
static float GetAngleFromPoints2DSimple(const cVector3f &avStartPos, const cVector3f &avGoalPos) {
	cVector3f vDelta = avGoalPos - avStartPos;

	if (vDelta.x == 0)
		vDelta.x = -kEpsilonf;
	if (vDelta.y == 0)
		vDelta.y = kEpsilonf;

	if (vDelta.y >= 0 && vDelta.x <= 0) {
		return -atan(vDelta.y / vDelta.x);
	} else if (vDelta.y < 0 && vDelta.x <= 0) {
		return -atan(vDelta.y / vDelta.x);
	} else {
		Error("Error in GetAngle 3D code! ARGHHH run in terror\n");
		return -1000;
	}
}

cVector3f cMath::GetAngleFromPoints3D(const cVector3f &avStartPos, const cVector3f &avGoalPos) {
	cVector3f vAngle = cVector3f(0, 0, 0);

	vAngle.y = -GetAngleFromPoints2D(cVector2f(avStartPos.x, avStartPos.z),
									 cVector2f(avGoalPos.x, avGoalPos.z));

	// Log("Y Angle: %f\n",vAngle.y);

	// vAngle.y = Wrap(vAngle.y,0.0f, k2Pif);

	cMatrixf mtxRot = MatrixRotateY(-vAngle.y);
	cVector3f vDelta = avGoalPos - avStartPos;

	// Log("vDelta: %s\n",vDelta.ToString().c_str());

	cVector3f vGoal = MatrixMul(mtxRot, vDelta);

	// Log("vGoal: %s\n",vGoal.ToString().c_str());

	vAngle.x = GetAngleFromPoints2DSimple(cVector3f(0, 0, 0), cVector2f(vGoal.z, vGoal.y));

	// Log("X Angle: %f\n",vAngle.x);

	vAngle.x = Wrap(vAngle.x, 0.0f, k2Pif);

	return vAngle;
}

//-----------------------------------------------------------------------

float cMath::PlaneToPointDist(const cPlanef &aPlane, const cVector3f &avVec) {
	return (aPlane.a * avVec.x) + (aPlane.b * avVec.y) + (aPlane.c * avVec.z) + aPlane.d;
}

//-----------------------------------------------------------------------

// TODO: This only works when there is no 0 in the planes.
void cMath::PlaneIntersectionLine(const cPlanef &aPA, const cPlanef &aPB,
								  cVector3f &avDir, cVector3f &avPoint) {
	avDir = Vector3Cross(cVector3f(aPA.a, aPA.b, aPA.c), cVector3f(aPB.a, aPB.b, aPB.c));

	// Set x to 0 so the calculation can be solved
	avPoint.x = 0;

	// find a value that sets b + b to 0.
	float fVal = (-aPB.b) / aPA.b;

	// Get z
	avPoint.z = ((aPA.d * fVal) + aPB.d) / ((aPA.c * fVal) + aPB.c);

	// Get y
	avPoint.y = (aPA.d - (aPA.c * avPoint.z)) / aPA.b;
}

//-----------------------------------------------------------------------

static inline bool IntersectsPlanePair(const cPlanef &aPlane1, const cPlanef &aPlane2,
									   const cVector3f &avPoint1, const cVector3f &avPoint2) {
	bool bPair[2][2];

	// 1 to 1
	if (cMath::PlaneToPointDist(aPlane1, avPoint1) >= 0)
		bPair[0][0] = true;
	else
		bPair[0][0] = false;
	// 1 to 2
	if (cMath::PlaneToPointDist(aPlane1, avPoint2) >= 0)
		bPair[0][1] = true;
	else
		bPair[0][1] = false;
	// 2 to 1
	if (cMath::PlaneToPointDist(aPlane2, avPoint1) >= 0)
		bPair[1][0] = true;
	else
		bPair[1][0] = false;
	// 2 to 2
	if (cMath::PlaneToPointDist(aPlane2, avPoint2) >= 0)
		bPair[1][1] = true;
	else
		bPair[1][1] = false;

	/*Log("-------------\n");
	Log("Pair 0 - 0: %d Pair 0 - 1: %d\n",bPair[0][0]?1:0,bPair[0][1]?1:0);
	Log("Pair 1 - 0: %d Pair 1 - 1: %d\n",bPair[1][0]?1:0,bPair[1][1]?1:0);*/

	// If either point is inside the plane pair we have an intersection.
	if ((bPair[0][0] && bPair[1][0]) || (bPair[0][1] && bPair[1][1]))
		return true;

	// If they are on different sides of the plane, it is intersected as well.
	if (((bPair[0][0] && !bPair[0][1]) && (!bPair[1][0] && bPair[1][1])) ||
		((!bPair[0][0] && bPair[0][1]) && (bPair[1][0] && !bPair[1][1]))) {
		return true;
	}

	return false;
}

bool cMath::CheckFrustumLineIntersection(const cPlanef *apPlanePairs, const cVector3f &avPoint1,
										 const cVector3f &avPoint2, int alPairNum) {
	for (int i = 0; i < alPairNum; ++i) {
		int lStart = i * 2;
		if (IntersectsPlanePair(apPlanePairs[lStart], apPlanePairs[lStart + 1], avPoint1, avPoint2) == false) {
			return false;
		}
	}

	return true;

	/*if( IntersectsPlanePair(apPlanePairs[0],apPlanePairs[1],avPoint1,avPoint2)
		&&
		IntersectsPlanePair(apPlanePairs[2],apPlanePairs[3],avPoint1,avPoint2)
		&&
		IntersectsPlanePair(apPlanePairs[4],apPlanePairs[5],avPoint1,avPoint2)
		)
	{
		return true;
	}
	return false;
	*/
}

//-----------------------------------------------------------------------

static const int kvQuadPairs[4][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}};

bool cMath::CheckFrustumQuadMeshIntersection(const cPlanef *apPlanePairs, tVector3fVec *apPoints,
											 int alPairNum) {
	int lPointNum = (int)apPoints->size();

	for (int quad = 0; quad < lPointNum; quad += 4) {
		cVector3f *pQuad = &(*apPoints)[quad];

		// Check the pairs
		for (int i = 0; i < 4; i++) {
			if (CheckFrustumLineIntersection(apPlanePairs, pQuad[kvQuadPairs[i][0]],
											 pQuad[kvQuadPairs[i][1]], alPairNum)) {
				return true;
			}
		}
	}

	return false;
}

//-----------------------------------------------------------------------

float cMath::QuaternionDot(const cQuaternion &aqA, const cQuaternion &aqB) {
	return aqA.w * aqB.w + aqA.v.x * aqB.v.x + aqA.v.y * aqB.v.y + aqA.v.z * aqB.v.z;
}

//-----------------------------------------------------------------------

cQuaternion cMath::QuaternionSlerp(float afT, const cQuaternion &aqA, const cQuaternion &aqB,
								   bool abShortestPath) {
	float fCos = QuaternionDot(aqA, aqB);

	// If the rotations are the same, just return the first.
	if (ABS(fCos - 1) <= kEpsilonf) {
		return aqA;
	}

	float fAngle = acos(fCos);

	float fSin = sin(fAngle);
	float fInvSin = 1.0f / fSin;
	float fCoeff0 = sin((1.0f - afT) * fAngle) * fInvSin;
	float fCoeff1 = sin(afT * fAngle) * fInvSin;
	// Do we need to invert rotation?
	if (fCos < 0.0f && abShortestPath) {
		fCoeff0 = -fCoeff0;
		// taking the complement requires renormalisation
		cQuaternion qT(aqA * fCoeff0 + aqB * fCoeff1);
		qT.Normalise();
		return qT;
	} else {
		return aqA * fCoeff0 + aqB * fCoeff1;
	}
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixSlerp(float afT, const cMatrixf &a_mtxA, const cMatrixf &a_mtxB,
							bool abShortestPath) {
	cVector3f vPos = a_mtxA.GetTranslation() * (1 - afT) + a_mtxB.GetTranslation() * afT;

	cQuaternion qA;
	qA.FromRotationMatrix(a_mtxA);
	cQuaternion qB;
	qB.FromRotationMatrix(a_mtxB);

	cQuaternion qFinal = cMath::QuaternionSlerp(afT, qA, qB, true);

	cMatrixf mtxFinal = cMath::MatrixQuaternion(qFinal);
	mtxFinal.SetTranslation(vPos);

	return mtxFinal;
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixMul(const cMatrixf &a_mtxA, const cMatrixf &a_mtxB) {
	cMatrixf mtxC;

	mtxC.m[0][0] = a_mtxA.m[0][0] * a_mtxB.m[0][0] + a_mtxA.m[0][1] * a_mtxB.m[1][0] + a_mtxA.m[0][2] * a_mtxB.m[2][0] + a_mtxA.m[0][3] * a_mtxB.m[3][0];
	mtxC.m[0][1] = a_mtxA.m[0][0] * a_mtxB.m[0][1] + a_mtxA.m[0][1] * a_mtxB.m[1][1] + a_mtxA.m[0][2] * a_mtxB.m[2][1] + a_mtxA.m[0][3] * a_mtxB.m[3][1];
	mtxC.m[0][2] = a_mtxA.m[0][0] * a_mtxB.m[0][2] + a_mtxA.m[0][1] * a_mtxB.m[1][2] + a_mtxA.m[0][2] * a_mtxB.m[2][2] + a_mtxA.m[0][3] * a_mtxB.m[3][2];
	mtxC.m[0][3] = a_mtxA.m[0][0] * a_mtxB.m[0][3] + a_mtxA.m[0][1] * a_mtxB.m[1][3] + a_mtxA.m[0][2] * a_mtxB.m[2][3] + a_mtxA.m[0][3] * a_mtxB.m[3][3];

	mtxC.m[1][0] = a_mtxA.m[1][0] * a_mtxB.m[0][0] + a_mtxA.m[1][1] * a_mtxB.m[1][0] + a_mtxA.m[1][2] * a_mtxB.m[2][0] + a_mtxA.m[1][3] * a_mtxB.m[3][0];
	mtxC.m[1][1] = a_mtxA.m[1][0] * a_mtxB.m[0][1] + a_mtxA.m[1][1] * a_mtxB.m[1][1] + a_mtxA.m[1][2] * a_mtxB.m[2][1] + a_mtxA.m[1][3] * a_mtxB.m[3][1];
	mtxC.m[1][2] = a_mtxA.m[1][0] * a_mtxB.m[0][2] + a_mtxA.m[1][1] * a_mtxB.m[1][2] + a_mtxA.m[1][2] * a_mtxB.m[2][2] + a_mtxA.m[1][3] * a_mtxB.m[3][2];
	mtxC.m[1][3] = a_mtxA.m[1][0] * a_mtxB.m[0][3] + a_mtxA.m[1][1] * a_mtxB.m[1][3] + a_mtxA.m[1][2] * a_mtxB.m[2][3] + a_mtxA.m[1][3] * a_mtxB.m[3][3];

	mtxC.m[2][0] = a_mtxA.m[2][0] * a_mtxB.m[0][0] + a_mtxA.m[2][1] * a_mtxB.m[1][0] + a_mtxA.m[2][2] * a_mtxB.m[2][0] + a_mtxA.m[2][3] * a_mtxB.m[3][0];
	mtxC.m[2][1] = a_mtxA.m[2][0] * a_mtxB.m[0][1] + a_mtxA.m[2][1] * a_mtxB.m[1][1] + a_mtxA.m[2][2] * a_mtxB.m[2][1] + a_mtxA.m[2][3] * a_mtxB.m[3][1];
	mtxC.m[2][2] = a_mtxA.m[2][0] * a_mtxB.m[0][2] + a_mtxA.m[2][1] * a_mtxB.m[1][2] + a_mtxA.m[2][2] * a_mtxB.m[2][2] + a_mtxA.m[2][3] * a_mtxB.m[3][2];
	mtxC.m[2][3] = a_mtxA.m[2][0] * a_mtxB.m[0][3] + a_mtxA.m[2][1] * a_mtxB.m[1][3] + a_mtxA.m[2][2] * a_mtxB.m[2][3] + a_mtxA.m[2][3] * a_mtxB.m[3][3];

	mtxC.m[3][0] = a_mtxA.m[3][0] * a_mtxB.m[0][0] + a_mtxA.m[3][1] * a_mtxB.m[1][0] + a_mtxA.m[3][2] * a_mtxB.m[2][0] + a_mtxA.m[3][3] * a_mtxB.m[3][0];
	mtxC.m[3][1] = a_mtxA.m[3][0] * a_mtxB.m[0][1] + a_mtxA.m[3][1] * a_mtxB.m[1][1] + a_mtxA.m[3][2] * a_mtxB.m[2][1] + a_mtxA.m[3][3] * a_mtxB.m[3][1];
	mtxC.m[3][2] = a_mtxA.m[3][0] * a_mtxB.m[0][2] + a_mtxA.m[3][1] * a_mtxB.m[1][2] + a_mtxA.m[3][2] * a_mtxB.m[2][2] + a_mtxA.m[3][3] * a_mtxB.m[3][2];
	mtxC.m[3][3] = a_mtxA.m[3][0] * a_mtxB.m[0][3] + a_mtxA.m[3][1] * a_mtxB.m[1][3] + a_mtxA.m[3][2] * a_mtxB.m[2][3] + a_mtxA.m[3][3] * a_mtxB.m[3][3];

	return mtxC;
}

//-----------------------------------------------------------------------

cVector3f cMath::MatrixMul(const cMatrixf &a_mtxA, const cVector3f &avB) {
	cVector3f vC;

	vC.x = (a_mtxA.m[0][0] * avB.x + a_mtxA.m[0][1] * avB.y + a_mtxA.m[0][2] * avB.z + a_mtxA.m[0][3]);
	vC.y = (a_mtxA.m[1][0] * avB.x + a_mtxA.m[1][1] * avB.y + a_mtxA.m[1][2] * avB.z + a_mtxA.m[1][3]);
	vC.z = (a_mtxA.m[2][0] * avB.x + a_mtxA.m[2][1] * avB.y + a_mtxA.m[2][2] * avB.z + a_mtxA.m[2][3]);

	return vC;
}

//-----------------------------------------------------------------------

cVector3f cMath::MatrixMulDivideW(const cMatrixf &a_mtxA, const cVector3f &avB) {
	cVector3f vC;

	float fInvW = 1.0f / (a_mtxA.m[3][0] * avB.x + a_mtxA.m[3][1] * avB.y + a_mtxA.m[3][2] * avB.z + a_mtxA.m[3][3]);

	vC.x = (a_mtxA.m[0][0] * avB.x + a_mtxA.m[0][1] * avB.y + a_mtxA.m[0][2] * avB.z + a_mtxA.m[0][3]) * fInvW;
	vC.y = (a_mtxA.m[1][0] * avB.x + a_mtxA.m[1][1] * avB.y + a_mtxA.m[1][2] * avB.z + a_mtxA.m[1][3]) * fInvW;
	vC.z = (a_mtxA.m[2][0] * avB.x + a_mtxA.m[2][1] * avB.y + a_mtxA.m[2][2] * avB.z + a_mtxA.m[2][3]) * fInvW;

	return vC;
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixMulScalar(const cMatrixf &a_mtxA, float afB) {
	cMatrixf mtxC;

	mtxC.m[0][0] = a_mtxA.m[0][0] * afB;
	mtxC.m[0][1] = a_mtxA.m[0][1] * afB;
	mtxC.m[0][2] = a_mtxA.m[0][2] * afB;
	mtxC.m[0][3] = a_mtxA.m[0][3] * afB;
	mtxC.m[1][0] = a_mtxA.m[1][0] * afB;
	mtxC.m[1][1] = a_mtxA.m[1][1] * afB;
	mtxC.m[1][2] = a_mtxA.m[1][2] * afB;
	mtxC.m[1][3] = a_mtxA.m[1][3] * afB;
	mtxC.m[2][0] = a_mtxA.m[2][0] * afB;
	mtxC.m[2][1] = a_mtxA.m[2][1] * afB;
	mtxC.m[2][2] = a_mtxA.m[2][2] * afB;
	mtxC.m[2][3] = a_mtxA.m[2][3] * afB;
	mtxC.m[3][0] = a_mtxA.m[3][0] * afB;
	mtxC.m[3][1] = a_mtxA.m[3][1] * afB;
	mtxC.m[3][2] = a_mtxA.m[3][2] * afB;
	mtxC.m[3][3] = a_mtxA.m[3][3] * afB;

	return mtxC;
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixRotate(cVector3f avRot, eEulerRotationOrder aOrder) {
	cMatrixf mtxRot = cMatrixf::Identity;
	switch (aOrder) {
	case eEulerRotationOrder_XYZ:
		mtxRot = MatrixMul(MatrixRotateX(avRot.x), mtxRot);
		mtxRot = MatrixMul(MatrixRotateY(avRot.y), mtxRot);
		mtxRot = MatrixMul(MatrixRotateZ(avRot.z), mtxRot);
		break;
	case eEulerRotationOrder_XZY:
		mtxRot = MatrixMul(MatrixRotateX(avRot.x), mtxRot);
		mtxRot = MatrixMul(MatrixRotateZ(avRot.z), mtxRot);
		mtxRot = MatrixMul(MatrixRotateY(avRot.y), mtxRot);
		break;
	case eEulerRotationOrder_YXZ:
		mtxRot = MatrixMul(MatrixRotateY(avRot.y), mtxRot);
		mtxRot = MatrixMul(MatrixRotateX(avRot.x), mtxRot);
		mtxRot = MatrixMul(MatrixRotateZ(avRot.z), mtxRot);
		break;
	case eEulerRotationOrder_YZX:
		mtxRot = MatrixMul(MatrixRotateY(avRot.y), mtxRot);
		mtxRot = MatrixMul(MatrixRotateZ(avRot.z), mtxRot);
		mtxRot = MatrixMul(MatrixRotateX(avRot.x), mtxRot);
		break;
	case eEulerRotationOrder_ZXY:
		mtxRot = MatrixMul(MatrixRotateZ(avRot.z), mtxRot);
		mtxRot = MatrixMul(MatrixRotateX(avRot.x), mtxRot);
		mtxRot = MatrixMul(MatrixRotateY(avRot.y), mtxRot);
		break;
	case eEulerRotationOrder_ZYX:
		mtxRot = MatrixMul(MatrixRotateZ(avRot.z), mtxRot);
		mtxRot = MatrixMul(MatrixRotateY(avRot.y), mtxRot);
		mtxRot = MatrixMul(MatrixRotateX(avRot.x), mtxRot);
		break;
	case eEulerRotationOrder_LastEnum:
		break;
	}

	return mtxRot;
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixRotateX(float afAngle) {
	return cMatrixf(1, 0, 0, 0,
					0, cos(afAngle), -sin(afAngle), 0,
					0, sin(afAngle), cos(afAngle), 0,
					0, 0, 0, 1);
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixRotateY(float afAngle) {
	return cMatrixf(cos(afAngle), 0, sin(afAngle), 0,
					0, 1, 0, 0,
					-sin(afAngle), 0, cos(afAngle), 0,
					0, 0, 0, 1);
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixRotateZ(float afAngle) {
	return cMatrixf(cos(afAngle), -sin(afAngle), 0, 0,
					sin(afAngle), cos(afAngle), 0, 0,
					0, 0, 1, 0,
					0, 0, 0, 1);
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixQuaternion(const cQuaternion &aqRot) {
	cMatrixf mtxOut;
	// Set the non rotation part.
	mtxOut.m[0][3] = 0;
	mtxOut.m[1][3] = 0;
	mtxOut.m[2][3] = 0;
	mtxOut.m[3][0] = 0;
	mtxOut.m[3][1] = 0;
	mtxOut.m[3][2] = 0;
	mtxOut.m[3][3] = 1;

	aqRot.ToRotationMatrix(mtxOut);

	return mtxOut;
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixScale(cVector3f avScale) {
	return cMatrixf(avScale.x, 0, 0, 0,
					0, avScale.y, 0, 0,
					0, 0, avScale.z, 0,
					0, 0, 0, 1);
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixTranslate(cVector3f avTrans) {
	return cMatrixf(1, 0, 0, avTrans.x,
					0, 1, 0, avTrans.y,
					0, 0, 1, avTrans.z,
					0, 0, 0, 1);
}
//-----------------------------------------------------------------------

float cMath::MatrixMinor(const cMatrixf &a_mtxA,
						 const size_t r0, const size_t r1, const size_t r2,
						 const size_t c0, const size_t c1, const size_t c2) {
	return a_mtxA.m[r0][c0] * (a_mtxA.m[r1][c1] * a_mtxA.m[r2][c2] - a_mtxA.m[r2][c1] * a_mtxA.m[r1][c2]) -
		   a_mtxA.m[r0][c1] * (a_mtxA.m[r1][c0] * a_mtxA.m[r2][c2] - a_mtxA.m[r2][c0] * a_mtxA.m[r1][c2]) +
		   a_mtxA.m[r0][c2] * (a_mtxA.m[r1][c0] * a_mtxA.m[r2][c1] - a_mtxA.m[r2][c0] * a_mtxA.m[r1][c1]);
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixAdjoint(const cMatrixf &a_mtxA) {
	return cMatrixf(MatrixMinor(a_mtxA, 1, 2, 3, 1, 2, 3),
					-MatrixMinor(a_mtxA, 0, 2, 3, 1, 2, 3),
					MatrixMinor(a_mtxA, 0, 1, 3, 1, 2, 3),
					-MatrixMinor(a_mtxA, 0, 1, 2, 1, 2, 3),

					-MatrixMinor(a_mtxA, 1, 2, 3, 0, 2, 3),
					MatrixMinor(a_mtxA, 0, 2, 3, 0, 2, 3),
					-MatrixMinor(a_mtxA, 0, 1, 3, 0, 2, 3),
					MatrixMinor(a_mtxA, 0, 1, 2, 0, 2, 3),

					MatrixMinor(a_mtxA, 1, 2, 3, 0, 1, 3),
					-MatrixMinor(a_mtxA, 0, 2, 3, 0, 1, 3),
					MatrixMinor(a_mtxA, 0, 1, 3, 0, 1, 3),
					-MatrixMinor(a_mtxA, 0, 1, 2, 0, 1, 3),

					-MatrixMinor(a_mtxA, 1, 2, 3, 0, 1, 2),
					MatrixMinor(a_mtxA, 0, 2, 3, 0, 1, 2),
					-MatrixMinor(a_mtxA, 0, 1, 3, 0, 1, 2),
					MatrixMinor(a_mtxA, 0, 1, 2, 0, 1, 2));
}

//-----------------------------------------------------------------------

float cMath::MatrixDeterminant(const cMatrixf &a_mtxA) {
	return a_mtxA.m[0][0] * MatrixMinor(a_mtxA, 1, 2, 3, 1, 2, 3) -
		   a_mtxA.m[0][1] * MatrixMinor(a_mtxA, 1, 2, 3, 0, 2, 3) +
		   a_mtxA.m[0][2] * MatrixMinor(a_mtxA, 1, 2, 3, 0, 1, 3) -
		   a_mtxA.m[0][3] * MatrixMinor(a_mtxA, 1, 2, 3, 0, 1, 2);
}

//-----------------------------------------------------------------------

cMatrixf cMath::MatrixInverse(const cMatrixf &a_mtxA) {
	return MatrixMulScalar(MatrixAdjoint(a_mtxA), (1.0f / MatrixDeterminant(a_mtxA)));
}

//-----------------------------------------------------------------------

cVector3f cMath::MatrixToEulerAngles(const cMatrixf &a_mtxA, eEulerRotationOrder aOrder) {
	cVector3f vAngles;

	/*if (a_mtxA.m[2][0] > 0.998f) // singularity at north pole
	{
		vAngles.y = atan2(a_mtxA.m[0][2],a_mtxA.m[2][2]);
		vAngles.z = kPif/2;
		vAngles.x = 0;
		Log("Special case 1\n");
	}
	if (a_mtxA.m[2][0] < -0.998f) // singularity at south pole
	{
		vAngles.y = atan2(a_mtxA.m[0][2],a_mtxA.m[2][2]);
		vAngles.z = -kPif/2;
		vAngles.x = 0;
		Log("Special case 2\n");
	}
	else*/
	{
		vAngles.x = atan2(a_mtxA.m[2][1], a_mtxA.m[2][2]);
		vAngles.y = -asin(a_mtxA.m[2][0]);
		vAngles.z = atan2(a_mtxA.m[1][0], a_mtxA.m[0][0]);
	}
	return vAngles;
}

//-----------------------------------------------------------------------

const char *cMath::MatrixToChar(const cMatrixf &a_mtxA) {
	snprintf(mpTempChar, 1024, "[%.3f, %.3f, %.3f, %.3f] [%.3f, %.3f, %.3f, %.3f] [%.3f, %.3f, %.3f, %.3f] [%.3f, %.3f, %.3f, %.3f]",
			 a_mtxA.m[0][0], a_mtxA.m[0][1], a_mtxA.m[0][2], a_mtxA.m[0][3],
			 a_mtxA.m[1][0], a_mtxA.m[1][1], a_mtxA.m[1][2], a_mtxA.m[1][3],
			 a_mtxA.m[2][0], a_mtxA.m[2][1], a_mtxA.m[2][2], a_mtxA.m[2][3],
			 a_mtxA.m[3][0], a_mtxA.m[3][1], a_mtxA.m[3][2], a_mtxA.m[3][3]);

	return mpTempChar;
}
//-----------------------------------------------------------------------

static inline cVector3f GetVector3(const float *apVertexArray, int alIdx, int alStride) {
	const float *apVec = &apVertexArray[alIdx * alStride];

	return cVector3f(apVec[0], apVec[1], apVec[2]);
}

/*static inline void AddVector3(float *apArray, int alIdx, const cVector3f &avVec, int alStride) {
	float *apVec = &apArray[alIdx * alStride];
	apVec[0] += avVec.x;
	apVec[1] += avVec.y;
	apVec[2] += avVec.z;
}*/

static inline void SetVector4(const cVector3f &avVec, float afW, float *apArray, int alIdx) {
	float *apVec = &apArray[alIdx * 4];
	apVec[0] = avVec.x;
	apVec[1] = avVec.y;
	apVec[2] = avVec.z;
	apVec[3] = afW;
}

static inline bool Vector3Equal(const float *apArrayA, int alIdxA, const float *apArrayB, int alIdxB,
								int alStride) {
	if (apArrayA[alIdxA * alStride + 0] == apArrayB[alIdxB * alStride + 0] &&
		apArrayA[alIdxA * alStride + 1] == apArrayB[alIdxB * alStride + 1] &&
		apArrayA[alIdxA * alStride + 2] == apArrayB[alIdxB * alStride + 2]) {
		return true;
	}

	return false;
}

bool cMath::CreateTriTangentVectors(float *apDestArray,
									const unsigned int *apIndexArray, int alIndexNum,
									const float *apVertexArray, int alVtxStride,
									const float *apTexArray,
									const float *apNormalArray,
									int alVertexNum) {
	// Create two temp arrays and clear them.
	tVector3fVec vTempTangents1;
	tVector3fVec vTempTangents2;

	// Log("Creating tangents:\n");
	// Log("Num of indices: %d\n",alIndexNum);
	// Log("Num of vertrices: %d\n",alVertexNum);

	Hpl1::resizeAndFill(vTempTangents1, alVertexNum, cVector3f(0, 0, 0));
	Hpl1::resizeAndFill(vTempTangents2, alVertexNum, cVector3f(0, 0, 0));

	// Iterate through the triangles
	for (int triIdx = 0; triIdx < alIndexNum; triIdx += 3) {
		// Log("Triangle %d: ",triIdx/3);

		// Get the indices of the triangle
		int idx1 = apIndexArray[triIdx + 0];
		int idx2 = apIndexArray[triIdx + 1];
		int idx3 = apIndexArray[triIdx + 2];

		// Log("1: '%d' 2: '%d'  3: '%d' ", idx1, idx2, idx3);

		// Get the 3 points making up the triangle
		cVector3f vPos1 = GetVector3(apVertexArray, idx1, alVtxStride);
		cVector3f vPos2 = GetVector3(apVertexArray, idx2, alVtxStride);
		cVector3f vPos3 = GetVector3(apVertexArray, idx3, alVtxStride);

		// Get the 3 texture coords in the triangle.
		cVector3f vTex1 = GetVector3(apTexArray, idx1, 3);
		cVector3f vTex2 = GetVector3(apTexArray, idx2, 3);
		cVector3f vTex3 = GetVector3(apTexArray, idx3, 3);

		// Get the vectors between the positions.
		cVector3f vPos1To2 = vPos2 - vPos1;
		cVector3f vPos1To3 = vPos3 - vPos1;

		// Get the vectors between the tex coords
		cVector3f vTex1To2 = vTex2 - vTex1;
		cVector3f vTex1To3 = vTex3 - vTex1;

		// Get the direction of the S and T tangents
		float fR = 1.0f / (vTex1To2.x * vTex1To3.y - vTex1To2.y * vTex1To3.x);

		cVector3f vSDir((vTex1To3.y * vPos1To2.x - vTex1To2.y * vPos1To3.x) * fR,
						(vTex1To3.y * vPos1To2.y - vTex1To2.y * vPos1To3.y) * fR,
						(vTex1To3.y * vPos1To2.z - vTex1To2.y * vPos1To3.z) * fR);

		cVector3f vTDir((vTex1To2.x * vPos1To3.x - vTex1To3.x * vPos1To2.x) * fR,
						(vTex1To2.x * vPos1To3.y - vTex1To3.x * vPos1To2.y) * fR,
						(vTex1To2.x * vPos1To3.z - vTex1To3.x * vPos1To2.z) * fR);

		// Add the temp arrays with the values:
		vTempTangents1[idx1] += vSDir;
		vTempTangents1[idx2] += vSDir;
		vTempTangents1[idx3] += vSDir;

		vTempTangents2[idx1] += vTDir;
		vTempTangents2[idx2] += vTDir;
		vTempTangents2[idx3] += vTDir;

		// Log("\n");
	}

	// Log("Looking for duplicates: \n");
	// Go through the vertrices and find normal and vertex copies. Smooth the tangents on these
	float fMaxCosAngle = -1.0f;
	for (int i = 0; i < alVertexNum; i++) {
		for (int j = i + 1; j < alVertexNum; j++) {
			// Log("(%.1f, %.1f, %.1f)", apVertexArray[i+0],apVertexArray[i+1],apVertexArray[i+2]);
			// Log(" vs ");
			// Log("(%.1f, %.1f, %.1f)\n", apVertexArray[j+0],apVertexArray[j+1],apVertexArray[j+2]);

			if (Vector3Equal(apVertexArray, i, apVertexArray, j, alVtxStride) &&
				Vector3Equal(apNormalArray, i, apNormalArray, j, 3)) {
				// Log("Found at %d and %d!\n", i, j);

				cVector3f vAT1 = vTempTangents1[i];
				cVector3f vAT2 = vTempTangents2[i];

				cVector3f vBT1 = vTempTangents1[j];
				cVector3f vBT2 = vTempTangents2[j];

				if (Vector3Dot(vAT1, vBT1) >= fMaxCosAngle) {
					vTempTangents1[j] += vAT1;
					vTempTangents1[i] += vBT1;
				}

				if (Vector3Dot(vAT2, vBT2) >= fMaxCosAngle) {
					vTempTangents2[j] += vAT2;
					vTempTangents2[i] += vBT2;
				}
			}
		}
	}

	// Iterate through the dest array and set tangent values
	for (int vtxIdx = 0; vtxIdx < alVertexNum; vtxIdx++) {

		cVector3f vNormal = GetVector3(apNormalArray, vtxIdx, 3);
		cVector3f &vTempTan1 = vTempTangents1[vtxIdx];
		cVector3f &vTempTan2 = vTempTangents2[vtxIdx];

		// Gram-Schmidt orthogonalize
		cVector3f vTan = vTempTan1 - (vNormal * cMath::Vector3Dot(vNormal, vTempTan1));
		vTan.Normalise();

		// Log("Add tangent %d: ",vtxIdx);
		// Log(" %.1f, %.1f, %.1f ",vTan.x, vTan.y, vTan.z);
		// Log("\n");

		// Calculate if left or right handed.
		float fW = (cMath::Vector3Dot(cMath::Vector3Cross(vNormal, vTempTan1), vTempTan2) < 0.0f) ? -1.0f : 1.0f;

		SetVector4(vTan, fW, apDestArray, vtxIdx);
	}

	return true;
}

//-----------------------------------------------------------------------

bool cMath::CreateTriangleData(tTriangleDataVec &avTriangles,
							   const unsigned int *apIndexArray, int alIndexNum,
							   const float *apVertexArray, int alVtxStride, int alVertexNum) {
	int lNumOfTri = alIndexNum / 3;
	if ((int)avTriangles.size() < lNumOfTri)
		avTriangles.resize(lNumOfTri);

	// Log("Creating triangle data:\n");
	for (int tri = 0, idx = 0; tri < lNumOfTri; tri++, idx += 3) {
		// Log("checking: tri %d idx %d\n",tri, idx);
		// Calculate normal
		const float *pVtx0 = &apVertexArray[apIndexArray[idx] * alVtxStride];
		const float *pVtx1 = &apVertexArray[apIndexArray[idx + 1] * alVtxStride];
		const float *pVtx2 = &apVertexArray[apIndexArray[idx + 2] * alVtxStride];

		cVector3f vEdge1(pVtx1[0] - pVtx0[0], pVtx1[1] - pVtx0[1], pVtx1[2] - pVtx0[2]);
		cVector3f vEdge2(pVtx2[0] - pVtx0[0], pVtx2[1] - pVtx0[1], pVtx2[2] - pVtx0[2]);

		avTriangles[tri].normal = Vector3Cross(vEdge2, vEdge1);
	}

	return true;
}

//-----------------------------------------------------------------------
/*static bool EdgePointEqual(const float *apVertexArray,
						   const cTriEdge &edge1, const cTriEdge &edge2, int alStride) {
	if (Vector3Equal(apVertexArray, edge1.point1, apVertexArray, edge2.point1, alStride) &&
		Vector3Equal(apVertexArray, edge1.point2, apVertexArray, edge2.point2, alStride)) {
		return true;
	}

	if (Vector3Equal(apVertexArray, edge1.point1, apVertexArray, edge2.point2, alStride) &&
		Vector3Equal(apVertexArray, edge1.point2, apVertexArray, edge2.point1, alStride)) {
		return true;
	}

	return false;
}

/////////////////////////

static bool EdgeTriEqual(const cTriEdge &edge1, const cTriEdge &edge2) {
	if (edge1.tri1 == edge2.tri1 && edge1.tri2 == edge2.tri2)
		return true;
	if (edge1.tri1 == edge1.tri1 && edge1.tri2 == edge2.tri1)
		return true;
	return false;
}

/////////////////////////

static bool EdgeEqual(const float *apVertexArray, const cTriEdge &edge1, const cTriEdge &edge2, int alStride) {
	if (EdgePointEqual(apVertexArray, edge1, edge2, alStride) && EdgeTriEqual(edge1, edge2))
		return true;

	return false;
}*/

/////////////////////////

static const float *gpVertexArray;
static const unsigned int *gpIndexArray;
static int glVertexStride;

//////////////////////////////////////////////////////

class cVertexIndices {
public:
	cVertexIndices(unsigned int alIdx) {
		mlstIndices.push_back(alIdx);
	}
	tUIntList mlstIndices;
};

typedef Common::StableMap<cVector3f, cVertexIndices> tVtxIdxMap;
typedef tVtxIdxMap::iterator tVtxIdxMapIt;

//////////////////////////////////////////////////////

class cEdgeCompare {
public:
	// The point1 must be > than point2 for this to work!
	bool operator()(const cTriEdge &Edge1, const cTriEdge &Edge2) const {
		cVector3f vPoint1_1 = GetVector3(gpVertexArray, Edge1.point1, glVertexStride);
		cVector3f vPoint1_2 = GetVector3(gpVertexArray, Edge1.point2, glVertexStride);
		cVector3f vPoint2_1 = GetVector3(gpVertexArray, Edge2.point1, glVertexStride);
		cVector3f vPoint2_2 = GetVector3(gpVertexArray, Edge2.point2, glVertexStride);

		// 1 - 1
		if (vPoint1_1.x != vPoint2_1.x)
			return vPoint1_1.x > vPoint2_1.x;
		if (vPoint1_1.y != vPoint2_1.y)
			return vPoint1_1.y > vPoint2_1.y;
		if (vPoint1_1.z != vPoint2_1.z)
			return vPoint1_1.z > vPoint2_1.z;
		// 2 - 2
		if (vPoint1_2.x != vPoint2_2.x)
			return vPoint1_2.x > vPoint2_2.x;
		if (vPoint1_2.y != vPoint2_2.y)
			return vPoint1_2.y > vPoint2_2.y;
		if (vPoint1_2.z != vPoint2_2.z)
			return vPoint1_2.z > vPoint2_2.z;

		return false;
	}
};

typedef Hpl1::Std::set<cTriEdge, cEdgeCompare> tTriEdgeListMap;
typedef tTriEdgeListMap::iterator tTriEdgeListMapIt;

//////////////////////////////////////////////////////

static void CheckEdgeSwitch(cTriEdge *apEdge) {
	cVector3f vPoint1 = GetVector3(gpVertexArray, apEdge->point1, glVertexStride);
	cVector3f vPoint2 = GetVector3(gpVertexArray, apEdge->point2, glVertexStride);
	if (vPoint1 < vPoint2) {
		unsigned int lTemp = apEdge->point1;
		apEdge->point1 = apEdge->point2;
		apEdge->point2 = lTemp;
	}
}

//////////////////////////////////////////////////////

void AddEdgeToMap(cTriEdge &aEdge, tTriEdgeListMap &aMap) {
	tTriEdgeListMapIt it = aMap.find(aEdge);
	// if edge is not added, create a new element.
	if (it == aMap.end()) {
		aEdge.tri2 = -1;
		aMap.insert(aEdge);
	}
	// if added check if there already exist an edge with the triangles
	else {
		if (it->tri2 != -1)
			return;
		if (it->tri1 == aEdge.tri1)
			return;

		// Set the other triangle! voila our edge is done!
		it->tri2 = aEdge.tri1;
	}
}

//////////////////////////////////////////////////////

bool cMath::CreateEdges(tTriEdgeVec &avEdges,
						const unsigned int *apIndexArray, int alIndexNum,
						const float *apVertexArray, int alVtxStride, int alVertexNum,
						bool *apIsDoubleSided) {
	const bool bLog = false;

	// Initial setup
	*apIsDoubleSided = false;

	tVtxIdxMap mapVtxIndices;
	tTriEdgeListMap mapTriEdgeLists;

	gpIndexArray = apIndexArray;
	gpVertexArray = apVertexArray;
	glVertexStride = alVtxStride;

	////////////////////////////////////////////////
	// Iterate indices and add all that reference the same vertex to
	//  the same element in a map
	for (int idx = 0; idx < alIndexNum; idx++) {
		cVector3f vVtx = GetVector3(apVertexArray, apIndexArray[idx], alVtxStride);
		if (bLog)
			Log("Checking idx: %d with vec: %s, ", idx, vVtx.ToString().c_str());

		tVtxIdxMapIt it = mapVtxIndices.find(vVtx);
		// If vertex already exist just add the index
		if (it != mapVtxIndices.end()) {
			if (bLog)
				Log("Already added, appending.!\n");
			it->second.mlstIndices.push_back(idx);
		}
		// if vertex is not added create new vertex and add.
		else {
			if (bLog)
				Log("Adding as new!\n");
			mapVtxIndices.insert(tVtxIdxMap::value_type(vVtx, cVertexIndices(idx)));
		}
	}
	if (bLog) {
		Log("FINAL VTX MAP:\n");
		tVtxIdxMapIt VtxIt = mapVtxIndices.begin();
		for (; VtxIt != mapVtxIndices.end(); ++VtxIt) {
			const cVector3f &vVtx = VtxIt->first;
			const cVertexIndices &Data = VtxIt->second;
			Log("Vtx: %s Idx: ", vVtx.ToString().c_str());
			for (tUIntList::const_iterator it = Data.mlstIndices.begin(); it != Data.mlstIndices.end(); ++it) {
				Log("%d, ", *it);
			}
			Log("\n");
		}
	}

	////////////////////////////////////////////////
	// Iterate vertices and check what indices belong to it.
	// create a edges from these.
	// TODO: iterate the amp here instead.
	tVtxIdxMapIt VtxIt = mapVtxIndices.begin();
	for (; VtxIt != mapVtxIndices.end(); ++VtxIt) {
		cVertexIndices &Data = VtxIt->second;

		// Iterate the indices and create edges.
		tUIntListIt it = Data.mlstIndices.begin();
		for (; it != Data.mlstIndices.end(); ++it) {
			int lTriIdx = ((*it) / 3) * 3;
			unsigned int lVtx = apIndexArray[*it]; // The num of the vertex this index reference to.

			// Create edges
			cTriEdge edge1, edge2;
			edge1.point1 = lVtx;
			edge2.point1 = lVtx;
			edge1.tri1 = lTriIdx / 3;
			edge2.tri1 = lTriIdx / 3;

			// Get the index the vertex has in the tri (0 -2)
			int lIdxInTri = (*it) % 3;
			// Log("Idx in tri: %d\n",lIdxInTri);

			// Get the end points of the edge.
			int lPoint1 = lIdxInTri + 1;
			if (lPoint1 > 2)
				lPoint1 = 0;
			int lPoint2 = lIdxInTri - 1;
			if (lPoint2 < 0)
				lPoint2 = 2;

			// Set the end points.
			edge1.point2 = apIndexArray[lTriIdx + lPoint1];
			edge2.point2 = apIndexArray[lTriIdx + lPoint2];

			// Switch points if they 1 < 2
			CheckEdgeSwitch(&edge1);
			CheckEdgeSwitch(&edge2);

			AddEdgeToMap(edge1, mapTriEdgeLists);
			AddEdgeToMap(edge2, mapTriEdgeLists);
		}
	}
	if (bLog) {
		Log("FINAL EDGE MAP:\n");
		tTriEdgeListMapIt EdgeIt = mapTriEdgeLists.begin();
		for (; EdgeIt != mapTriEdgeLists.end(); ++EdgeIt) {
			cTriEdge &Edge = const_cast<cTriEdge &>(*EdgeIt);

			Log("P1: %d P2: %d Tri1: %d Tri2: %d\n", Edge.point1, Edge.point2, Edge.tri1, Edge.tri2);
		}
	}

	////////////////////////////////////////////////
	// Iterate edge map and check triangles it has, create edges from this.
	avEdges.reserve(mapTriEdgeLists.size());
	tTriEdgeListMapIt EdgeIt = mapTriEdgeLists.begin();
	for (; EdgeIt != mapTriEdgeLists.end(); ++EdgeIt) {
		cTriEdge &Edge = const_cast<cTriEdge &>(*EdgeIt);
		const unsigned int *pTri1 = &apIndexArray[Edge.tri1 * 3];

		if (Edge.tri2 == -1) {
			Edge.invert_tri2 = true;
			*apIsDoubleSided = true;
		} else {
			Edge.invert_tri2 = false;
		}

		// Get position of point1 in triangle
		int lPoint1InTri = 0;
		for (int i = 0; i < 3; i++) {
			if (Vector3Equal(apVertexArray, pTri1[i], apVertexArray, Edge.point1, alVtxStride)) {
				lPoint1InTri = i;
				break;
			}
		}
		// The next position in the triangle.
		int lNextInTri = lPoint1InTri + 1;
		if (lNextInTri >= 3)
			lNextInTri = 0;

		// Log("Point in: %d Next: %d\n",lPoint1InTri,lNextInTri);

		// If next point is NOT point 2, then the edge
		// must be switched.
		if (Vector3Equal(apVertexArray, pTri1[lNextInTri], apVertexArray, Edge.point2, alVtxStride)) {
			unsigned int lTemp = Edge.point1;
			Edge.point1 = Edge.point2;
			Edge.point2 = lTemp;
			// if(bLog)Log("Switching points\n");
		}

		// Add the final edge.
		avEdges.push_back(Edge);
	}

	return true;
}

/*bool cMath::CreateEdges(tTriEdgeVec &avEdges,
						const unsigned int* apIndexArray,int alIndexNum,
						const float* apVertexArray, int alVtxStride,int alVertexNum,
						bool *apIsDoubleSided)
{
	const bool bLog= false;

	//Setup
	*apIsDoubleSided = false;

	////////////////////////////////////////////////////////////////////////
	//For each vertex check what triangles reference it and make edges to the
	//vertices in that triangle.
	for(int vtx=0; vtx < alVertexNum; vtx++)
	{
		if(bLog)Log("Checking vtx: %d\n",vtx);
		int lCount=0; //The number of times the vertex is referred to.
		tUIntList mlstIndices;
		for(int idx=0; idx < alIndexNum; idx++)
		{
			if(Vector3Equal(apVertexArray,vtx, apVertexArray, apIndexArray[idx],alVtxStride))
			{
				lCount++;
				mlstIndices.push_back(idx);
			}
		}
		if(lCount==0){
			Warning("Found unreferenced vertex when building edges!\n");
		}
		else
		{
			///////////////////////////////////////////
			//Create the edges
			//Log("Vertex has %d references!\n",lCount);
			tUIntListIt it = mlstIndices.begin();
			for(; it != mlstIndices.end(); ++it)
			{
				//Get the triangle start index.
				int lTriIdx = ((*it)/3)*3;

				//if(bLog)Log("Tri index: %d!\n",lTriIdx);
				//Create edges
				cTriEdge edge1,edge2;
				edge1.point1 = vtx;
				edge2.point1 = vtx;
				edge1.tri1 = lTriIdx/3;
				edge2.tri1 = lTriIdx/3;

				//Get the index the vertex has in the tri (0 -2)
				int lIdxInTri = (*it) % 3;
				//Log("Idx in tri: %d\n",lIdxInTri);

				//Get the end points of the edge.
				int lPoint1 = lIdxInTri+1;
				if(lPoint1>2) lPoint1 =0;
				int lPoint2 = lIdxInTri-1;
				if(lPoint2<0) lPoint2 =2;

				//if(bLog)Log("P1: %d P2: %d!\n",apIndexArray[lTriIdx +lPoint1],
				//							apIndexArray[lTriIdx +lPoint2]);

				//Set the end points.
				edge1.point2 = apIndexArray[lTriIdx + lPoint1];
				edge2.point2 = apIndexArray[lTriIdx + lPoint2];

				avEdges.push_back(edge1);
				avEdges.push_back(edge2);
			}
		}
	}

	////////////////////////////////////////////////////////////////////////
	//Check for triangles that share the same edge. Pair these.
	if(bLog)Log("Looking for shared edges on triangles.\n");
	tTriEdgeVec vTempEdges;

	//Check for the last element as well incase it has no pair..
	for(int e1 =0; e1 < (int)avEdges.size(); e1++)
	{
		bool bFound = false;
		cTriEdge edge1 = avEdges[e1];

		if(bLog)Log("Checking p(%d)-p(%d)|(%d)\n",
			edge1.point1,edge1.point2,
			edge1.tri1);

		for(int e2 =e1+1; e2 < (int)avEdges.size(); e2++)
		{
			cTriEdge edge2 = avEdges[e2];

			//if(bLog)Log("Checking %d to %d\n",e1,e2);

			//Check if the edges has different triangles but the same points.
			if(edge1.tri1 != edge2.tri1 && EdgePointEqual(apVertexArray,edge1,edge2,alVtxStride))
			{
				bFound = true;

				if(bLog)Log("Found a pair: p(%d)-p(%d)|(%d)\n",edge2.point1,edge2.point2,
													edge2.tri1);

				edge1.tri2 = edge2.tri1;
				edge1.invert_tri2 = false;

				break;
			}
		}

		//If no edge was found it is at a hole.
		if(bFound == false)
		{
			edge1.tri2 = edge1.tri1;
			edge1.invert_tri2 = true;

			if(bLog) Log("No pair found, at a hole\n");
		}

		bFound = false;

		//SLOW AS HELL THIS IS...
		for(size_t i=0; i< vTempEdges.size(); i++)
		{
			if(EdgeEqual(apVertexArray,vTempEdges[i],edge1,alVtxStride)){
				bFound = true;
				break;
			}

			//if it is the last edge, don't add it if there is another edge with
			//the same points and different triangle sides.
			if(EdgePointEqual(apVertexArray,vTempEdges[i],edge1,alVtxStride) &&
				vTempEdges[i].tri1 != vTempEdges[i].tri2)
			{
				bFound = true;
				break;
			}
		}

		//If not already added, add
		if(bFound== false){

			//If a face with inverted tri was added, the mesh is double sided.
			if(edge1.invert_tri2){
				*apIsDoubleSided = true;
			}

			vTempEdges.push_back(edge1);
			if(bLog)Log("Added!\n");
		}
		if(bLog)Log("------------\n");
	}

	////////////////////////////////////////////////////////////////////////
	//Clear the old list and add the new nice pairs.
	avEdges.clear();
	avEdges.reserve(vTempEdges.size());

	//Log("FINAL EDGES:\n");
	//Create the final edges. This means making sure so that
	//edge point 1 is before point 2 in triangle 1 and 2 point 2 before
	//1 in triangle 2.
	for(size_t edge=0; edge< vTempEdges.size(); edge++)
	{
		cTriEdge Edge = vTempEdges[edge];
		const unsigned int *pTri1 = &apIndexArray[Edge.tri1 * 3];
		const unsigned int *pTri2 = &apIndexArray[Edge.tri2 * 3];

		if(bLog)Log("Edge %d:\n",edge);

		if(bLog)Log("Tri1: 0:(%.2f %.2f %.2f) 1:(%.2f %.2f %.2f) 2:(%.2f %.2f %.2f)\n",
					apVertexArray[pTri1[0]*alVtxStride+0],apVertexArray[pTri1[0]*alVtxStride+1],apVertexArray[pTri1[0]*alVtxStride+2],
					apVertexArray[pTri1[1]*alVtxStride+0],apVertexArray[pTri1[1]*alVtxStride+1],apVertexArray[pTri1[1]*alVtxStride+2],
					apVertexArray[pTri1[2]*alVtxStride+0],apVertexArray[pTri1[2]*alVtxStride+1],apVertexArray[pTri1[2]*alVtxStride+2]);

		if(bLog)Log("Tri2: 0:(%.2f %.2f %.2f) 1:(%.2f %.2f %.2f) 2:(%.2f %.2f %.2f)\n",
					apVertexArray[pTri2[0]*alVtxStride+0],apVertexArray[pTri2[0]*alVtxStride+1],apVertexArray[pTri2[0]*alVtxStride+2],
					apVertexArray[pTri2[1]*alVtxStride+0],apVertexArray[pTri2[1]*alVtxStride+1],apVertexArray[pTri2[1]*alVtxStride+2],
					apVertexArray[pTri2[2]*alVtxStride+0],apVertexArray[pTri2[2]*alVtxStride+1],apVertexArray[pTri2[2]*alVtxStride+2]);

		if(bLog)Log("Point1: (%.2f %.2f %.2f) Point2: (%.2f %.2f %.2f)\n",
					apVertexArray[Edge.point1*alVtxStride+0],apVertexArray[Edge.point1*alVtxStride+1],apVertexArray[Edge.point1*alVtxStride+2],
					apVertexArray[Edge.point2*alVtxStride+0],apVertexArray[Edge.point2*alVtxStride+1],apVertexArray[Edge.point2*alVtxStride+2]);

		//Get position of point1 in triangle
		int lPoint1InTri=0;
		for(int i=0; i < 3; i++){
			if(Vector3Equal(apVertexArray,pTri1[i],apVertexArray, Edge.point1,alVtxStride))
			{
				lPoint1InTri = i;
				break;
			}
		}
		//The next position in the triangle.
		int lNextInTri = lPoint1InTri +1;
		if(lNextInTri >=3 ) lNextInTri =0;

		if(bLog)Log("Point in: %d Next: %d\n",lPoint1InTri,lNextInTri);

		//If next point is NOT point 2, then the edge
		//must be switched.
		if(Vector3Equal(apVertexArray,pTri1[ lNextInTri],apVertexArray,Edge.point2,alVtxStride))
		{
			unsigned int lTemp = Edge.point1;
			Edge.point1 = Edge.point2;
			Edge.point2 = lTemp;
			if(bLog)Log("Switching points\n");
		}

		if(bLog)Log("%d: p(%d)-p(%d)|(%d)-(%d)\n",edge,
			Edge.point1,Edge.point2,
			Edge.tri1, Edge.tri2);

		//Log("-----------\n");

		avEdges.push_back(Edge);
	}

	return true;
}*/

//-----------------------------------------------------------------------

} // namespace hpl
