
#include "../core.mod/include/irrlicht.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

void RotatePoint(f32 rx,f32 ry,f32 rz,f32* x,f32* y,f32* z)
{
	f32 xPrime = (*x * cos(rz)) - (*y * sin(rz));
	f32 yPrime = (*x * sin(rz)) + (*y * cos(rz));
	f32 zPrime = *z;
	*x = xPrime;
	*y = yPrime;
	*z = zPrime;
	
	// Y
	xPrime = (*x * cos(ry)) +  (*z * sin(ry));
	yPrime = *y;
	zPrime = (-*x * sin(ry))+  (*z * cos(ry));
	*x = xPrime;
	*y = yPrime;
	*z = zPrime;
	
	// X
	xPrime = *x;
	yPrime = (*y * cos(rx)) - (*z * sin(rx));
	zPrime = (*y * sin(rx)) + (*z * cos(rx));
	*x = xPrime;
	*y = yPrime;
	*z = zPrime;
}

extern "C"
{
	void RotateMesh(IMesh* mesh, IMeshManipulator* mm,f32 rx,f32 ry,f32 rz)
	{

		s32 bufcnt=mesh->getMeshBufferCount();
		vector3df center=mesh->getBoundingBox().getCenter();
		
		for (s32 curbuf=0; curbuf<bufcnt; curbuf++)
		{
			IMeshBuffer* mbuf=mesh->getMeshBuffer(curbuf);
			s32 vertcnt=mbuf->getVertexCount();
			E_VERTEX_TYPE vtype=mbuf->getVertexType();

			S3DVertex* vertex_arr1=0;
			S3DVertex2TCoords* vertex_arr2=0;
			S3DVertexTangents* vertex_arr3=0;

			switch (vtype)
			{
				case EVT_STANDARD:
					vertex_arr1=(S3DVertex*)mbuf->getVertices();
					break;
				case EVT_2TCOORDS:
					vertex_arr2=(S3DVertex2TCoords*)mbuf->getVertices();
					break;
				case EVT_TANGENTS:
					vertex_arr3=(S3DVertexTangents*)mbuf->getVertices();
					break;
			}

			f32 newx;
			f32 newy;
			f32 newz;

			for (s32 vert=0; vert<vertcnt; vert++)
			{
				if (vertex_arr1)
				{
					newx=vertex_arr1[vert].Pos.X;	
					newy=vertex_arr1[vert].Pos.Y;	
					newz=vertex_arr1[vert].Pos.Z;
				}
				else if (vertex_arr2)
				{
					newx=vertex_arr2[vert].Pos.X;	
					newy=vertex_arr2[vert].Pos.Y;	
					newz=vertex_arr2[vert].Pos.Z;
				}
				else if (vertex_arr3)
				{
					newx=vertex_arr3[vert].Pos.X;	
					newy=vertex_arr3[vert].Pos.Y;	
					newz=vertex_arr3[vert].Pos.Z;
				}
				
				vector3df point=vector3df(newx,newy,newz);
				point.rotateYZBy(rx,center);
				point.rotateXZBy(ry,center);
				point.rotateXYBy(rz,center);
				newx=point.X;
				newy=point.Y;
				newz=point.Z;
				
				//RotatePoint(rx,ry,rz,&newx,&newy,&newz);
				if (vertex_arr1)
				{
					vertex_arr1[vert].Pos.X=newx;
					vertex_arr1[vert].Pos.Y=newy;
					vertex_arr1[vert].Pos.Z=newz;
				}
				else if (vertex_arr2)
				{
					vertex_arr2[vert].Pos.X=newx;
					vertex_arr2[vert].Pos.Y=newy;
					vertex_arr2[vert].Pos.Z=newz;
				}
				else if (vertex_arr3)
				{
					vertex_arr3[vert].Pos.X=newx;
					vertex_arr3[vert].Pos.Y=newy;
					vertex_arr3[vert].Pos.Z=newz;
				}
			}
			mbuf->recalculateBoundingBox();
		}
		mm->recalculateNormals(mesh);
	}
	
	void RotateAnimMesh(IAnimatedMesh* animmesh, IMeshManipulator* mm,f32 rx,f32 ry,f32 rz)
	{

		s32 framecnt=animmesh->getFrameCount();

		for (s32 frame=0; frame<framecnt; frame++)
		{
			IMesh* mesh=animmesh->getMesh(frame);

			if (mesh)
				RotateMesh(mesh,mm,rx,ry,rz);
		}
	}
	
	//! Tests if a aabox is within the Frustum.
   //! \return 0 = Whole box is outside the frustum, 1 = Box intersects with frustum, 2 = Whole box is inside the frustum.
   u32 SViewFrustum_classifyBoxRelation( SViewFrustum* view,core::aabbox3d<f32>* aabox )
   {
      // Get edges
      core::vector3d<f32> edges[8];
      aabox->getEdges(edges);         // let's generate the edge points

      // Helpers
      u32 uInsidePlane = 0;
      u32 uPointsInside;      // Number of points which are in front of current plane
      u32 uBoxIsInside;      // Flag: 1 if whole box is in front of current plane

      for( u32 p=0; p<6; ++p )
      {
         uPointsInside = 8;
         uBoxIsInside = 1;

         for( u32 i=0; i<8; ++i )
         {
            // check if current point is behind the plane
            if( view->planes[p].classifyPointRelation(edges[i]) == core::ISREL3D_BACK )
            {
               uBoxIsInside = 0;
               --uPointsInside;
            }
         }

         // if all 8 points of the box are outside of a plane -> box is outside frustum
         if( 0 == uPointsInside ) return(0);

         uInsidePlane += uBoxIsInside;
      }

      // if box is in front of all 6 planes -> box is fully inside
      if( 6 == uInsidePlane ) return(2);

      // box intersects with a plane
      return(1);
   } 
   		
}

// by Xaron: http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=11333
/** 
 * @brief guided missile steering helper function 
 * 
 * @param missilePosition - current absolute position of the missile 
 * @param missileRotation - current absolute rotation of the missile 
 * @param targetPosition  - current absolute target position 
 * @param oversteer       - oversteer factor in degrees 
 * 
 * @return targetPitchRoll - delta pitch (x) and delta roll (z) values to the target 
 * 
 *******************************************************************************************************/ 
/*
core::vector3df getToTargetPitchRoll( core::vector3df& missilePosition, core::vector3df& missileRotation, core::vector3df& targetPosition, float oversteer = 0.0f ) 
{ 
  core::matrix4 rotMatrix, invRotMatrix; 
  rotMatrix.setRotationDegrees( missileRotation ); 
  core::vector3df targetPitchRoll( 0.0f, 0.0f, 0.0f ); 
  // get the current missile direction 
  core::vector3df currentMissileTargetVec( 0.0f, 0.0f, 1.0f ); 

  // get the vector to the target (from the missile position) 
  core::vector3df missileTargetVec = ( targetPosition - missilePosition ).normalize(); 
  // invert the rotation matrix to transform the target vector into the missile's local space 
  rotMatrix.getInverse( invRotMatrix ); 
  // transform the target vector into missile's local space 
  invRotMatrix.rotateVect( missileTargetVec ); 
  // compute the roll angle to the target 
  float deltaRoll = atan2f( missileTargetVec.Y, missileTargetVec.X ) * core::RADTODEG - 90.0f; 
  if( deltaRoll < 0.0f ) 
    deltaRoll += 360.0f; 
  if( deltaRoll >= 360.0f ) 
    deltaRoll -= 360.0f; 

  if( deltaRoll > 180.0f ) 
    deltaRoll -= 360.0f; 

  // compute the pitch angle to the target 
  float deltaPitch = acosf( currentMissileTargetVec.dotProduct( missileTargetVec ) ) 
    * core::RADTODEG + oversteer; 

  targetPitchRoll.X = -deltaPitch; 
  targetPitchRoll.Z = deltaRoll; 

  return targetPitchRoll; 
}
*/

