#ifdef WIN32
#include <windows.h>
#endif
#include "Main.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "ObjLoader.h"
extern int NumObjects;
/* Ignored until Version6 */
void ConvertMeshTo(ObjFile id,unsigned char What)
{
}
void SetLightPosition(float lx,float ly,float lz)
{
}
void SetTextures(ObjFile id,const char BumpTex[],const char BaseTex[])
{
}
/*===================================================================================================
**
** Internal Data Structures
**
**=================================================================================================*/
typedef struct
{
float x, /* The x component of the vertex position */
y, /* The y component of the vertex position */
z; /* The z component of the vertex position */
} ObjVertex;
typedef struct
{
float x, /* The x component of the normal vector */
y, /* The y component of the normal vector */
z; /* The z component of the normal vector */
} ObjNormal;
typedef struct
{
float u, /* The u parametric texturing co-ordinate */
v; /* The v parametric texturing co-ordinate */
} ObjTexCoord;
typedef struct
{
unsigned int m_aVertexIndices[3], /* array of indicies that reference the vertex array in the mesh */
m_aNormalIndices[3], /* array of indicies that reference the normal array in the mesh */
m_aTexCoordIndicies[3]; /* array of indicies that reference the uv coordinate array in the mesh */
} ObjFace;
typedef struct _ObjMesh
{
ObjVertex *m_aVertexArray; /* Array of vertices that make up this mesh */
ObjNormal *m_aNormalArray; /* Array of normals that make up this mesh */
ObjTexCoord *m_aTexCoordArray; /* Array of texturing co-ordinates that make up this mesh */
ObjFace *m_aFaces; /* Array of faces that make up this mesh */
unsigned int m_iNumberOfVertices, /* The number of vertices in the m_aVertexArray array */
m_iNumberOfNormals, /* The number of normals in the m_aNormalArray array */
m_iNumberOfTexCoords, /* The number of uv's in the m_aTexCoordArray array */
m_iNumberOfFaces; /* The number of faces in the m_aFaces array */
struct _ObjMesh *m_pNext; /* Each mesh will be stored internally as a node on a linked list */
ObjFile m_iMeshID; /* the ID of the mesh */
} ObjMesh;
unsigned int ResizeTriangleArray(ObjMesh *pMesh,unsigned int iExtraTriangleCount)
{
/*
** Create a temporary pointer to the old face array in the mesh
*/
ObjFace *pTempFaces = pMesh->m_aFaces;
/*
** Keep track of the old face count
*/
unsigned int iTemp = pMesh->m_iNumberOfFaces;
/*
** Increase the number of faces that will be stored in the mesh
*/
pMesh->m_iNumberOfFaces += iExtraTriangleCount;
/*
** Allocate the number of faces now required by this mesh. Note, you could use
** realloc for this, but it's just my personal preference to do it this way.
*/
pMesh->m_aFaces = (ObjFace*)malloc(pMesh->m_iNumberOfFaces*sizeof(ObjFace));
/*
** If we assert here then the memory allocation for the meshes failed
*/
assert(pMesh->m_aFaces);
/*
** Copy across the data that was stored in the old array
*/
memcpy(pMesh->m_aFaces,pTempFaces,iTemp*sizeof(ObjFace));
/*
** Delete the data held in the previous array
*/
free(pTempFaces);
memset(&pMesh->m_aFaces[ iTemp ],0,iExtraTriangleCount*sizeof(ObjFace) );
/*
** return the old size of the array (thats the same as the index of the first new triangle).
*/
return iTemp;
}
// Global Variables
ObjMesh *g_LinkedListHead = NULL;
/*
** This is used to generate a unique ID for each Obj File
*/
unsigned int g_ObjIdGenerator=0;
/*
** This function is only called from within the *.c file and is used to create an ObjMesh structure and
** initialise its values (adds the mesh to the linked list also).
*/
ObjMesh *MakeOBJ( void )
{
/*
** The pointer we will create the mesh at the end of
*/
ObjMesh *pMesh = NULL;
pMesh = (ObjMesh*) malloc (sizeof(ObjMesh));
/* If the program asserts here, then there was a memory allocation failure */
assert(pMesh);
/*
** Initialise all pointers to NULL
*/
pMesh->m_aFaces = NULL;
pMesh->m_aNormalArray = NULL;
pMesh->m_aTexCoordArray = NULL;
pMesh->m_aVertexArray = NULL;
pMesh->m_iNumberOfFaces = 0;
pMesh->m_iNumberOfNormals = 0;
pMesh->m_iNumberOfTexCoords = 0;
pMesh->m_iNumberOfVertices = 0;
pMesh->m_iMeshID = ++g_ObjIdGenerator;
/*
** Insert the mesh at the beginning of the linked list
*/
pMesh->m_pNext = g_LinkedListHead;
g_LinkedListHead = pMesh;
return pMesh;
}
void GetIndices(char *pString, unsigned int *pVert, unsigned int *pNorm, unsigned int *pUV)
{
char *pV = pString,
*pN = NULL,
*pT = NULL;
while( *pString != '\0' )
{
if(*pString == '/')
{
*pString = '\0';
if(!pT)
{
pT = pString+1;
}
else
{
pN = pString+1;
}
}
pString++;
}
/*
** Read the vertex index
*/
sscanf(pV,"%d",pVert);
if( pN == NULL || *pN == '\0' )
{
*pNorm = 1;
}
else
{
sscanf(pN,"%d",pNorm);
}
if( pT == NULL || *pT == '\0' )
{
*pUV = 1;
}
else
{
sscanf(pT,"%d",pUV);
}
}
ObjFile LoadOBJ(const char *filename)
{
ObjMesh *pMesh=NULL;
unsigned int vc=0,nc=0,tc=0,fc=0;
char buffer[256];
FILE *fp = NULL;
/*
** Open the file for reading
*/
fp = fopen(filename,"r");
/*
** If the program asserted here, then the file could not be found or opened
*/
assert(fp);
/*
** Create the mesh structure and add it to the linked list
*/
pMesh = MakeOBJ();
while(!feof(fp))
{
memset(buffer,0,200);
/* Grab a line at a time */
fgets(buffer,256,fp);
/* look for the 'vn' - vertex normal - flag */
if( strncmp("vn ",buffer,3) == 0 )
{
++pMesh->m_iNumberOfNormals;
}
else
/* look for the 'vt' - texturing co-ordinate - flag */
if( strncmp("vt ",buffer,3) == 0 )
{
++pMesh->m_iNumberOfTexCoords;
}
else
/* look for the 'v ' - vertex co-ordinate - flag */
if( strncmp("v ",buffer,2) == 0 )
{
++pMesh->m_iNumberOfVertices;
}
else
/* look for the 'f ' - face - flag */
if( strncmp("f ",buffer,2) == 0 )
{
++pMesh->m_iNumberOfFaces;
}
}
/*
** close the file for now....
*/
fclose(fp);
/*
** Allocate the memory for the data arrays and check that it allocated ok
*/
pMesh->m_aVertexArray = (ObjVertex* )malloc( pMesh->m_iNumberOfVertices * sizeof(ObjVertex) );
assert(pMesh->m_aVertexArray);
/* there are occasionally times when the obj does not have any normals in it */
if( pMesh->m_iNumberOfNormals > 0 )
{
pMesh->m_aNormalArray = (ObjNormal* )malloc( pMesh->m_iNumberOfNormals * sizeof(ObjNormal) );
assert(pMesh->m_aNormalArray);
}
/* there are occasionally times when the obj does not have any tex coords in it */
if( pMesh->m_iNumberOfTexCoords > 0 )
{
pMesh->m_aTexCoordArray = (ObjTexCoord*)malloc( pMesh->m_iNumberOfTexCoords * sizeof(ObjTexCoord) );
assert(pMesh->m_aTexCoordArray);
}
pMesh->m_aFaces = (ObjFace* )malloc( pMesh->m_iNumberOfFaces * sizeof(ObjFace) );
assert(pMesh->m_aFaces);
fp = fopen(filename,"r");
while(!feof(fp))
{
memset(buffer,0,255);
/* Grab a line at a time */
fgets(buffer,256,fp);
/* look for the 'vn' - vertex normal - flag */
if( strncmp("vn ",buffer,3) == 0 )
{
sscanf((buffer+2),"%f%f%f",
&pMesh->m_aNormalArray[ nc ].x,
&pMesh->m_aNormalArray[ nc ].y,
&pMesh->m_aNormalArray[ nc ].z);
++nc;
}
else
/* look for the 'vt' - texturing co-ordinate - flag */
if( strncmp("vt ",buffer,3) == 0 )
{
sscanf((buffer+2),"%f%f",
&pMesh->m_aTexCoordArray[ tc ].u,
&pMesh->m_aTexCoordArray[ tc ].v);
++tc;
}
else
/* look for the 'v ' - vertex co-ordinate - flag */
if( strncmp("v ",buffer,2) == 0 )
{
sscanf((buffer+1),"%f%f%f",
&pMesh->m_aVertexArray[ vc ].x,
&pMesh->m_aVertexArray[ vc ].y,
&pMesh->m_aVertexArray[ vc ].z);
++vc;
}
else
/* look for the 'f ' - face - flag */
if( strncmp("f ",buffer,2) == 0 )
{
/*
** some data for later....
*/
char *pSplitString = NULL;
unsigned int i,ii = 0;
unsigned int iNewTrianglesIndex = 0,
iNumberOfExtraTriangles = 0;
unsigned int aFirstIndices[3],
aSecondIndices[3];
ObjFace *pf = NULL;
for(i=0;i<strlen(buffer);i++)
{
if(buffer == '/')
ii++;
}
ii/=2;
if( (ii-2) > 1 )
{
iNumberOfExtraTriangles = ii-3;
iNewTrianglesIndex = ResizeTriangleArray(pMesh,iNumberOfExtraTriangles);
}
pf = &pMesh->m_aFaces[ fc ];
pSplitString = strtok((buffer+2)," \t\n");
i=0;
/*
** Get the vertex structures that we know WILL exists (there has to be a minimum of three)
*/
for(i=0;i<3;i++)
{
GetIndices( pSplitString,
&pf->m_aVertexIndices,
&pf->m_aNormalIndices,
&pf->m_aTexCoordIndicies);
/* need to reduce the indices by 1 because array indices start at 0, obj starts at 1 */
--pf->m_aTexCoordIndicies;
--pf->m_aNormalIndices;
--pf->m_aVertexIndices;
switch(i)
{
case 0:
aFirstIndices[0] = pf->m_aVertexIndices;
aFirstIndices[1] = pf->m_aNormalIndices;
aFirstIndices[2] = pf->m_aTexCoordIndicies;
break;
case 2:
aSecondIndices[0] = pf->m_aVertexIndices;
aSecondIndices[1] = pf->m_aNormalIndices;
aSecondIndices[2] = pf->m_aTexCoordIndicies;
break;
default:
break;
}
pSplitString = strtok(NULL," \t\n");
}
++fc;
/*
** Get the additional triangles that may be there
*/
for(i=0;i<iNumberOfExtraTriangles;i++)
{
pf = &pMesh->m_aFaces[ iNewTrianglesIndex + i ];
GetIndices( pSplitString,
&pf->m_aVertexIndices[2],
&pf->m_aNormalIndices[2],
&pf->m_aTexCoordIndicies[2]);
/* need to reduce the indices by 1 because array indices start at 0, obj starts at 1 */
--pf->m_aTexCoordIndicies[2];
--pf->m_aNormalIndices[2];
--pf->m_aVertexIndices[2];
/*
** Now assign the other indices for the triangle
*/
pf->m_aVertexIndices[0] = aFirstIndices[0];
pf->m_aNormalIndices[0] = aFirstIndices[1];
pf->m_aTexCoordIndicies[0] = aFirstIndices[2];
pf->m_aVertexIndices[1] = aSecondIndices[0];
pf->m_aNormalIndices[1] = aSecondIndices[1];
pf->m_aTexCoordIndicies[1] = aSecondIndices[2];
/*
** Copy over the indices for the next triangle.
*/
aSecondIndices[0] = pf->m_aVertexIndices[2];
aSecondIndices[1] = pf->m_aNormalIndices[2];
aSecondIndices[2] = pf->m_aTexCoordIndicies[2];
pSplitString = strtok(NULL," \t\n");
}
}
}
fclose(fp);
return pMesh->m_iMeshID;
}
void DrawOBJ(ObjFile id)
{
ObjMesh *pMesh = g_LinkedListHead;
while(pMesh && pMesh->m_iMeshID != id)
{
pMesh = pMesh->m_pNext;
}
/*
** Check to see if the mesh ID is valid.
*/
if(pMesh != NULL)
{
if( pMesh->m_aNormalArray != NULL &&
pMesh->m_aTexCoordArray != NULL )
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with normals & uv co-ordinates
*/
for(j=0;j<3;j++)
{
glTexCoord2f( pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].u,
pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].v);
glNormal3f( pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].x,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].y,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].z);
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
else
if( pMesh->m_aNormalArray != NULL )
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with Normals only
*/
for(j=0;j<3;j++)
{
glNormal3f( pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].x,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].y,
pMesh->m_aNormalArray[ pf->m_aNormalIndices[j] ].z);
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
else
if( pMesh->m_aTexCoordArray != NULL )
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons with Texturing co-ordinates only
*/
for(j=0;j<3;j++)
{
glTexCoord2f( pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].u,
pMesh->m_aTexCoordArray[ pf->m_aTexCoordIndicies[j] ].v);
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
else
{
unsigned int i;
glBegin(GL_TRIANGLES);
for(i=0;i<pMesh->m_iNumberOfFaces;i++)
{
unsigned int j;
ObjFace *pf = &pMesh->m_aFaces;
/*
** Draw the polygons
*/
for(j=0;j<3;j++)
{
glVertex3f( pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].x,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].y,
pMesh->m_aVertexArray[ pf->m_aVertexIndices[j] ].z);
}
}
glEnd();
}
}
}
void DeleteMesh(ObjMesh* pMesh)
{
/*
** If the pointer is valid
*/
if(pMesh)
{
/* delete the face array */
if(pMesh->m_aFaces)
{
free(pMesh->m_aFaces);
pMesh->m_aFaces = NULL;
}
/* delete the vertex array */
if(pMesh->m_aVertexArray)
{
free(pMesh->m_aVertexArray);
pMesh->m_aVertexArray = NULL;
}
/* delete the normal array */
if(pMesh->m_aNormalArray)
{
free(pMesh->m_aNormalArray);
pMesh->m_aNormalArray = NULL;
}
/* delete the texturing co-ordinate array */
if(pMesh->m_aTexCoordArray)
{
free(pMesh->m_aTexCoordArray);
pMesh->m_aTexCoordArray = NULL;
}
/* free the mesh */
free( pMesh );
}
}
void DeleteOBJ(ObjFile id)
{
/*
** Create two pointers to walk through the linked list
*/
ObjMesh *pCurr,
*pPrev = NULL;
/*
** Start traversing the list from the start
*/
pCurr = g_LinkedListHead;
while(pCurr != NULL && pCurr->m_iMeshID != id)
{
pPrev = pCurr;
pCurr = pCurr->m_pNext;
}
/*
** If we found the node that needs to be deleted
*/
if(pCurr != NULL)
{
/*
** If the pointer before it is NULL, then we need to
** remove the first node in the list
*/
if(pPrev == NULL)
{
g_LinkedListHead = pCurr->m_pNext;
}
/*
** Otherwise we are removing a node from somewhere else
*/
else
{
pPrev->m_pNext = pCurr->m_pNext;
}
/*
** Free the memory allocated for this mesh
*/
DeleteMesh(pCurr);
}
}
/*
** Delete all of the meshes starting from the front.
*/
void CleanUpOBJ(void)
{
ObjMesh *pCurr;
while(g_LinkedListHead != NULL)
{
pCurr = g_LinkedListHead;
g_LinkedListHead = g_LinkedListHead->m_pNext;
DeleteMesh(pCurr);
}
}
Collision Detection (Sphere-Triangle)
1) For each triangle, find closest point of the triangle from the sphere centre.
2) if the distance between the sphere centre and the point is greater than the sphere radius, then there is no collision.
3) Else, push the sphere away from that point, so the point is at distance = radius from the sphere centre.
void CheckInterference(Vector& SpherePos, const float SphereCentre, const CTriangle* Triangles, const int NumTriangles, const int NumIterations/*=3*/, const float RelaxationCoefficient/*=0.66f*/){ for(int j = 0; j < NumIterations; j ++) { for(int i = 0; i < NumTriangles; i ++) { // find closest point on the triangle to the sphere centre Vector ClosestPoint = Triangles.ClosestPoint(SphereCentre); // calculate tye distance between the two points. Vector Dist = SphereCentre - ClosestPoint; float DistanceSquared = Dist.DotProduct(Dist); // triangle too far away to collide if(DistanceSquared > Sphere.Radius * Sphere.Radius) return false; float Distance = sqrt(DistanceSquared); float Depth = Sphere.Radius - Distance; // depth of intersection. Vector Push = Dist * (Depth / Distance); // calculate the interference vector SpherePos += Push * RelaxationCoefficient; // push sphere pos away (with some damping). } }}the NumIteration runs the algo several times, because pushing away from one triangle might push you towards another triangle, so you need to do teh check several time, until it 'settles'. The relaxation coefficient add some damping in the algo, so the pushes are not too brutal. It will mean that the sphere will be intersecting triangles slightly, but it's a small price to pay.
as the sphere move, that function will make sure the sphere does not go through the triangles (to a degree). As for the ClosestPoint() function, for a later post.
AFAIK it has nothing to do about how you loaded the mesh.
BTW, sphere-point distance is a bit difficult, because you have to:
Project point on triangles plane
In the triangle? return distance
Project point on the three edges
Is it on at least one of them? Return closest
Else return the distance from the closest corner
//-------------------------------------------------------------------// Calculate the squared distance of a point to the triangle//// Nicked code from David Eberly's most excelent physics site// Don;t ask me how it works. It just does :)//// (s, t) => ClosestPoint = V[0] + s * (V[1] - V[0]) + t * (V[2] - V[0]).// // (http://www.magic-software.com)//-------------------------------------------------------------------float CTriangle::DistanceFromPoint(const Vector& P, float* pfSParam, float* pfTParam) const{ if (!gpxVerts) return 10000000.0f; //------------------------------------------------------------------- // 2 edges of the triangle //------------------------------------------------------------------- Vector E0 = (gpxVerts[i[1]]) - (gpxVerts[i[0]]); Vector E1 = (gpxVerts[i[2]]) - (gpxVerts[i[0]]); Vector kDiff = (gpxVerts[i[0]]) - P; float fA00 = E0 * E0; float fA01 = E0 * E1; float fA11 = E1 * E1; float fB0 = kDiff * E0; float fB1 = kDiff * E1; float fC = kDiff * kDiff; float fDet = (float) fabs(fA00*fA11-fA01*fA01); float fS = fA01*fB1-fA11*fB0; float fT = fA01*fB0-fA00*fB1; float fSqrDist; //------------------------------------------------------------------- // degenerate triangle //------------------------------------------------------------------- if (fabs(fDet) < 0.000000001f) return 100000000.0f; if ( fS + fT <= fDet ) { if ( fS < (float)0.0 ) { if ( fT < (float)0.0 ) // region 4 { if ( fB0 < (float)0.0 ) { fT = (float)0.0; if ( -fB0 >= fA00 ) { fS = (float)1.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = -fB0/fA00; fSqrDist = fB0*fS+fC; } } else { fS = (float)0.0; if ( fB1 >= (float)0.0 ) { fT = (float)0.0; fSqrDist = fC; } else if ( -fB1 >= fA11 ) { fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fT = -fB1/fA11; fSqrDist = fB1*fT+fC; } } } else // region 3 { fS = (float)0.0; if ( fB1 >= (float)0.0 ) { fT = (float)0.0; fSqrDist = fC; } else if ( -fB1 >= fA11 ) { fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fT = -fB1/fA11; fSqrDist = fB1*fT+fC; } } } else if ( fT < (float)0.0 ) // region 5 { fT = (float)0.0; if ( fB0 >= (float)0.0 ) { fS = (float)0.0; fSqrDist = fC; } else if ( -fB0 >= fA00 ) { fS = (float)1.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = -fB0/fA00; fSqrDist = fB0*fS+fC; } } else // region 0 { // minimum at interior point float fInvDet = ((float)1.0)/fDet; fS *= fInvDet; fT *= fInvDet; fSqrDist = fS*(fA00*fS+fA01*fT+((float)2.0)*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } else { float fTmp0, fTmp1, fNumer, fDenom; if ( fS < (float)0.0 ) // region 2 { fTmp0 = fA01 + fB0; fTmp1 = fA11 + fB1; if ( fTmp1 > fTmp0 ) { fNumer = fTmp1 - fTmp0; fDenom = fA00-2.0f*fA01+fA11; if ( fNumer >= fDenom ) { fS = (float)1.0; fT = (float)0.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = fNumer/fDenom; fT = (float)1.0 - fS; fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } else { fS = (float)0.0; if ( fTmp1 <= (float)0.0 ) { fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else if ( fB1 >= (float)0.0 ) { fT = (float)0.0; fSqrDist = fC; } else { fT = -fB1/fA11; fSqrDist = fB1*fT+fC; } } } else if ( fT < (float)0.0 ) // region 6 { fTmp0 = fA01 + fB1; fTmp1 = fA00 + fB0; if ( fTmp1 > fTmp0 ) { fNumer = fTmp1 - fTmp0; fDenom = fA00-((float)2.0)*fA01+fA11; if ( fNumer >= fDenom ) { fT = (float)1.0; fS = (float)0.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fT = fNumer/fDenom; fS = (float)1.0 - fT; fSqrDist = fS*(fA00*fS+fA01*fT+((float)2.0)*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } else { fT = (float)0.0; if ( fTmp1 <= (float)0.0 ) { fS = (float)1.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else if ( fB0 >= (float)0.0 ) { fS = (float)0.0; fSqrDist = fC; } else { fS = -fB0/fA00; fSqrDist = fB0*fS+fC; } } } else // region 1 { fNumer = fA11 + fB1 - fA01 - fB0; if ( fNumer <= (float)0.0 ) { fS = (float)0.0; fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fDenom = fA00-2.0f*fA01+fA11; if ( fNumer >= fDenom ) { fS = (float)1.0; fT = (float)0.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = fNumer/fDenom; fT = (float)1.0 - fS; fSqrDist = fS*(fA00*fS+fA01*fT+((float)2.0)*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } } } if ( pfSParam ) *pfSParam = fS; if ( pfTParam ) *pfTParam = fT; return (float) fabs(fSqrDist);}As I said, For each triangle, find closest point to the sphere centre, if point isnide sphere, move sphere centre away from the point, that's all there is. You will also need to cancel out the velocity probably, if you use a velocity / position system.
EDIT: Olii, we posted at the same time.
[Edited by - Arangol on January 3, 2007 12:56:45 PM]
collision = (point-sphere.centre).Magnitude < r;
or, optimising out the square root in Magnitude:
collision = (point-sphere.centre).MagSquared < r²
So yeah, to detect if a point
is in a sphere [C, r].
Vector D = (C - P); // the vector between C and P
float d2 = D.DotProduct(D); // squared distance of C from P, also squared length of vector D
float r2 = r * r; // squared radius.
if (d2 > r2) return false; // squared distance > squared radius. point not in sphere.
if the point is inside the sphere, you need to move the sphere away from the point. Meaning, you have to put P so that the distance between C and P is equal to 'r'.
For that, you push the sphere centre 'C' along the vector D, to a position where |CP| = r. for that you can use that simple formula.
float d = sqrt(d2); // length of vector D, which is square root of d2
C += D * (r - d) / d;
If you use explicit velocities (like, a euler integrator), don't forget to cancel the velocity component along the normal of collision, like so:
float vd = V.DotProduct(D);
V -= D * (vd / d2);
and the point C will be pushed away from P, and will be at distance 'r' from P.
Using the 'ClosestPoint' function, you can find your point 'P', and apply the algo above.
I'll re-write the ClosestPoint function for you.
//-------------------------------------------------------------------// Calculate the squared distance of a point to the triangle//// Nicked code from David Eberly's most excelent physics site// Don't ask me how it works. It just does :)// (http://www.magic-software.com)//// Parameters.// -----------// : Point in question.// [V0, V1, V2] : triangle vertices.//// return values.// --------------// [s, t] : closest point, in Parametric form (for tex coord reconstruction).// [ClosestPoint] : The point on the triangle the closest, as a vector.// Returns : The squared distance of the point to the triangle.//-------------------------------------------------------------------float PointTriangleDistanceSquared(const Vector& P, const Vector& V0, const Vector& V1, const Vector& V2, float* s, float* t, Vector* Closest){ //------------------------------------------------------------------- // 2 edges of the triangle //------------------------------------------------------------------- Vector E0 = (V1 - V0); Vector E1 = (V2 - V0); Vector kDiff = (V0 - P); float fA00 = E0.DotProduct(E0); float fA01 = E0.DotProduct(E1); float fA11 = E1.DotProduct(E1); float fB0 = kDiff.DotProduct(E0); float fB1 = kDiff.DotProduct(E1); float fC = kDiff.DotProduct(kDiff); float fDet = (float) fabs(fA00*fA11-fA01*fA01); float fS = fA01*fB1-fA11*fB0; float fT = fA01*fB0-fA00*fB1; float fSqrDist; //------------------------------------------------------------------- // degenerate triangle //------------------------------------------------------------------- if (fabs(fDet) < 0.000000001f) return 100000000.0f; if ( fS + fT <= fDet ) { if ( fS < (float)0.0 ) { if ( fT < (float)0.0 ) // region 4 { if ( fB0 < (float)0.0 ) { fT = (float)0.0; if ( -fB0 >= fA00 ) { fS = (float)1.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = -fB0/fA00; fSqrDist = fB0*fS+fC; } } else { fS = (float)0.0; if ( fB1 >= (float)0.0 ) { fT = (float)0.0; fSqrDist = fC; } else if ( -fB1 >= fA11 ) { fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fT = -fB1/fA11; fSqrDist = fB1*fT+fC; } } } else // region 3 { fS = (float)0.0; if ( fB1 >= (float)0.0 ) { fT = (float)0.0; fSqrDist = fC; } else if ( -fB1 >= fA11 ) { fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fT = -fB1/fA11; fSqrDist = fB1*fT+fC; } } } else if ( fT < (float)0.0 ) // region 5 { fT = (float)0.0; if ( fB0 >= (float)0.0 ) { fS = (float)0.0; fSqrDist = fC; } else if ( -fB0 >= fA00 ) { fS = (float)1.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = -fB0/fA00; fSqrDist = fB0*fS+fC; } } else // region 0 { // minimum at interior point float fInvDet = ((float)1.0)/fDet; fS *= fInvDet; fT *= fInvDet; fSqrDist = fS*(fA00*fS+fA01*fT+((float)2.0)*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } else { float fTmp0, fTmp1, fNumer, fDenom; if ( fS < (float)0.0 ) // region 2 { fTmp0 = fA01 + fB0; fTmp1 = fA11 + fB1; if ( fTmp1 > fTmp0 ) { fNumer = fTmp1 - fTmp0; fDenom = fA00-2.0f*fA01+fA11; if ( fNumer >= fDenom ) { fS = (float)1.0; fT = (float)0.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = fNumer/fDenom; fT = (float)1.0 - fS; fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } else { fS = (float)0.0; if ( fTmp1 <= (float)0.0 ) { fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else if ( fB1 >= (float)0.0 ) { fT = (float)0.0; fSqrDist = fC; } else { fT = -fB1/fA11; fSqrDist = fB1*fT+fC; } } } else if ( fT < (float)0.0 ) // region 6 { fTmp0 = fA01 + fB1; fTmp1 = fA00 + fB0; if ( fTmp1 > fTmp0 ) { fNumer = fTmp1 - fTmp0; fDenom = fA00-((float)2.0)*fA01+fA11; if ( fNumer >= fDenom ) { fT = (float)1.0; fS = (float)0.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fT = fNumer/fDenom; fS = (float)1.0 - fT; fSqrDist = fS*(fA00*fS+fA01*fT+((float)2.0)*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } else { fT = (float)0.0; if ( fTmp1 <= (float)0.0 ) { fS = (float)1.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else if ( fB0 >= (float)0.0 ) { fS = (float)0.0; fSqrDist = fC; } else { fS = -fB0/fA00; fSqrDist = fB0*fS+fC; } } } else // region 1 { fNumer = fA11 + fB1 - fA01 - fB0; if ( fNumer <= (float)0.0 ) { fS = (float)0.0; fT = (float)1.0; fSqrDist = fA11+((float)2.0)*fB1+fC; } else { fDenom = fA00-2.0f*fA01+fA11; if ( fNumer >= fDenom ) { fS = (float)1.0; fT = (float)0.0; fSqrDist = fA00+((float)2.0)*fB0+fC; } else { fS = fNumer/fDenom; fT = (float)1.0 - fS; fSqrDist = fS*(fA00*fS+fA01*fT+((float)2.0)*fB0) + fT*(fA01*fS+fA11*fT+((float)2.0)*fB1)+fC; } } } } if ( s ) *s = fS; if ( t ) *t = fT; if ( ClosestPoint ) *ClosestPoint = V0 + (fS * E0) + (fT * E1); return (float) fabs(fSqrDist);}That should work.
The point is.. for the vast majority of "space" (positions where the centre of the sphere could be), and ordinarily "most of the time".. the sphere and triangle do not / cannot collide. So really you want to be able to detect / determine that "as fast as possible".
Personally I'd convert the function to return a boolean (they collide / they don't), pass in the sphere radius (or its square) so I could perform the radius tests directly, rather than returning a value then testing that.
For most of "the time", or "the space", you don't need to calculate the actual distance to the closest point of the triangle, (or multiple distances and pick the lowest [rolleyes]).
You can perform some relatively fast rejection tests early on, and determine that the sphere and triangle cannot possibly collide.
The "biggy" here is point-plane distance.
You calculate the distance from the centre of the sphere to the plane of the triangle, pretty much right at the start.
If that distance is greater than the sphere's radius, you're done. (they don't collide)
If not... then you need to move on, and actually consider the triangle "proper", probably using Oliii's code..
It looks something like this... just a couple of extra lines..
Vector E0 = (V1 - V0); Vector E1 = (V2 - V0); Vector kDiff = (V0 - P); Vector norm = E0.CrossProduct(E1); float fD = norm.DotProduct(kdiff); // Test the distance (fD) against the radius // I'd do the test this way to avoid the sqrt and the division if ((fD * fD) > (Radius * Radius * norm.DotProduct(norm))) return 1000000.0f; // if they can collide.. continue on and test the triangle float fA00 = E0.DotProduct(E0); float fA01 = E0.DotProduct(E1);There is one further "spatial" optimization... (provided you've added the above)
If it turns out that the centre of the sphere projects to be inside the triangle (region 0 in the code)..
Then you don't need to calculate the distance to determine collision, they must / do collide.
[Edited by - daftasbrush on January 4, 2007 9:21:40 AM]
Also, the code ain't great for fast moving spheres. As usual, this is just one of the simplest possible way to do collision detection, there are much better methods but more complicated.
I have a crappy demo actually, doing that, in a very simple way, but with a AABBtree to speed up the code (still, the code is very slow).
http://uk.geocities.com/olivier_rebellion/simple_collision.zip
It's rubbish, mind you... but you'll get the idea.
I haven't read about vectors so I probably should.
http://members.gamedev.net/oliii/very_basic_collision.rar
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.