//-----------------------------------------------------------------------------
// File: Collision.cpp
//
// Desc: Implementation of the collision detection
//
// Copyright (c) 2000 Telemachos of Peroxide
// www.peroxide.dk
//-----------------------------------------------------------------------------

#include "vectormath.h"

// ----------------------------------------------------------------------------
// Descr : Structure keeping track of the collisions as we find them. 
// Note  : Somewhat simplified from what is needed if you deside to implement
//         the features described in the section of dynamic geometry.
// ----------------------------------------------------------------------------

struct TCollisionPacket {
 
 // data about player movement
 D3DVECTOR velocity;
 D3DVECTOR sourcePoint;
  
 // radius of ellipsoid.  
 D3DVECTOR eRadius 
  
 // for error handling  
 D3DVECTOR lastSafePosition;
 BOOL stuck; 
    
 // data for collision response 
 BOOL foundCollision;
 double    nearestDistance; // nearest distance to hit
 D3DVECTOR nearestIntersectionPoint; // on sphere
 D3DVECTOR nearestPolygonIntersectionPoint; // on polygon
};





// ----------------------------------------------------------------------
// Name  : CheckCollision()
// Descr : Checks one mesh for collision
// Return: updated collision structure.
// -----------------------------------------------------------------------  

void Mesh::CheckCollision(TCollisionPacket* colPackage) {
 
 // plane data
 int A, B, C;
 D3DVECTOR p1,p2,p3;
 D3DVECTOR pNormal;
 D3DVECTOR pOrigin;
 D3DVECTOR v1, v2;

 
 // from package
 D3DVECTOR source = colPackage->sourcePoint;
 D3DVECTOR eRadius = colPackage->eRadius;
 D3DVECTOR velocity = colPackage->velocity;	
 
 // keep a copy of this as it's needed a few times
 D3DVECTOR normalizedVelocity = velocity;
 normalizeVector(normalizedVelocity);
 
 // intersection data
 D3DVECTOR sIPoint;    // sphere intersection point
 D3DVECTOR pIPoint;    // plane intersection point 	
 D3DVECTOR polyIPoint; // polygon intersection point
 
 // how long is our velocity
 double distanceToTravel = lengthOfVector(velocity);	
 
 double distToPlaneIntersection;
 double distToEllipsoidIntersection;
  
 // loop through all faces in mesh  	
 for (int i=0; i<m_dwNumFaces; i++)
  {
               
     A = vertexIndices[i*3];
     B = vertexIndices[i*3+1];
     C = vertexIndices[i*3+2];
     
     // Get the data for the triangle in question and scale to ellipsoid space
     p1.x = m_pIndexedVertices[A].x / eRadius.x;
     p1.y = m_pIndexedVertices[A].y / eRadius.y;
     p1.z = m_pIndexedVertices[A].z / eRadius.z;
         
     p2.x = m_pIndexedVertices[B].x / eRadius.x;
     p2.y = m_pIndexedVertices[B].y / eRadius.y;
     p2.z = m_pIndexedVertices[B].z / eRadius.z;
             
     p3.x = m_pIndexedVertices[C].x / eRadius.x;
     p3.y = m_pIndexedVertices[C].y / eRadius.y;
     p3.z = m_pIndexedVertices[C].z / eRadius.z;
               
               
     // Make the plane containing this triangle.      
     pOrigin = p1;
     v1 = p2-p1;
     v2 = p3-p1;
    
        
     // You might not need this if you KNOW all your triangles are valid
     if (!(isZeroVector(v1) || isZeroVector(v2))) {
                         
     	// determine normal to plane containing polygon  
     	pNormal = wedge(v1, v2);
     	normalizeVector(pNormal);
     
    
       
     		// calculate sphere intersection point
     		sIPoint = source - pNormal;     
        
     		// classify point to determine if ellipsoid span the plane
     		DWORD pClass = classifyPoint(sIPoint, pOrigin, pNormal);
         
     
     		// find the plane intersection point
     		if (pClass == PLANE_BACKSIDE) { // plane is embedded in ellipsoid
     
      			// find plane intersection point by shooting a ray from the 
      			// sphere intersection point along the planes normal.
      			distToPlaneIntersection = intersectRayPlane(sIPoint, pNormal, pOrigin, pNormal);
                       
      			// calculate plane intersection point
      			pIPoint.x = sIPoint.x + distToPlaneIntersection * pNormal.x; 
      			pIPoint.y = sIPoint.y + distToPlaneIntersection * pNormal.y; 
      			pIPoint.z = sIPoint.z + distToPlaneIntersection * pNormal.z; 	
       
     		} 
     		else { 
     
     			// shoot ray along the velocity vector
     			distToPlaneIntersection = intersectRayPlane(sIPoint, normalizedVelocity, pOrigin, pNormal);
               
     			// calculate plane intersection point
     			pIPoint.x = sIPoint.x + distToPlaneIntersection * normalizedVelocity.x; 
     			pIPoint.y = sIPoint.y + distToPlaneIntersection * normalizedVelocity.y; 
     			pIPoint.z = sIPoint.z + distToPlaneIntersection * normalizedVelocity.z; 	
          	
     		}
     
       
     
     		// find polygon intersection point. By default we assume its equal to the 
     		// plane intersection point
     
     		polyIPoint = pIPoint;
     		distToEllipsoidIntersection = distToPlaneIntersection;
     
     		if (!CheckPointInTriangle(pIPoint,p1,p2,p3)) { // if not in triangle
     	
      			polyIPoint = closestPointOnTriangle(p1, p2, p3, pIPoint);	
           
      			distToEllipsoidIntersection = intersectRaySphere(polyIPoint, -normalizedVelocity, source, 1.0f);  
                  
      			if (distToEllipsoidIntersection > 0) { 	
     			// calculate true sphere intersection point
     				sIPoint.x = polyIPoint.x + distToEllipsoidIntersection * -normalizedVelocity.x;
     				sIPoint.y = polyIPoint.y + distToEllipsoidIntersection * -normalizedVelocity.y;
     				sIPoint.z = polyIPoint.z + distToEllipsoidIntersection * -normalizedVelocity.z;
     			}
    
     		} 
     
    
  		// Here we do the error checking to see if we got ourself stuck last frame
   		if (CheckPointInSphere(polyIPoint, source, 1.0f)) 
			colPackage->stuck = TRUE;
       
    
		// Ok, now we might update the collision data if we hit something
    		if ((distToEllipsoidIntersection > 0) && (distToEllipsoidIntersection <= distanceToTravel)) { 
     			if ((colPackage->foundCollision == FALSE) || (distToEllipsoidIntersection < colPackage->nearestDistance))  {
           
                		// if we are hit we have a closest hit so far. We save the information
      				colPackage->nearestDistance = distToEllipsoidIntersection;
      		       		colPackage->nearestIntersectionPoint = sIPoint;
      				colPackage->nearestPolygonIntersectionPoint = polyIPoint;
      				colPackage->foundCollision = TRUE;
			}
    		} 
   } // if a valid plane 	
 } // for all faces	
}
