Collision Detection, Simple Physics


Spheres are controlled before every movement of scene, whether they are not touching other spheres or any plane. Direction of movement (velocity) has every sphere saved in its energyVector. In case of collision with other sphere the function getVelocitiesAfterCollision is used to compute new velocities. This function computes velocities of spheres after elastic collision in 2D. In case of collision of sphere with a wall the reflexed direction is computed as R = N*2*(-D.N) + D. After the movement of scene also the correct rotation of spheres is computed (Sphere->Rotate). Sphere is rotated in the direction of movement and its actual position is saved as rotation matrix in Vec3f rotX, rotY, rotZ.

Source code (core part):

// from function moveScene(Scene*), MicroTrace.cxx

// collision between spheres

for (unsigned int i=0; i < scene->mSpheres.size(); i++)

{

       for (unsigned int j=0; j < scene->mSpheres[i]->primitive.size(); j++)

       {

             Sphere* sph1 = (Sphere*)(scene->mSpheres[i]->primitive[j]);

 

             // dont compute collision

             if (sph1->crashInLastTime > 0)

                    continue;

 

             // check collision with every other spheres

             for (unsigned int k=i; k < scene->mSpheres.size(); k++)

             {

                    unsigned int l; // we calculate only not yet checked pairs

 

                    if (k==i) {

                           l=j;

                    }

                    else {

                           l=0;

                    }

 

                    for (; l < scene->mSpheres[k]->primitive.size(); l++) {

                           if (i != k || j != l)

                           {

                                  // other spheres - not i,j sphere

                                  Sphere* sph2 = (Sphere*)(scene->mSpheres[k]->primitive[l]);

 

                                  intersectionSize = sph1->radius + sph2->radius - (sph1->center - sph2->center).length();

                                  if (intersectionSize > 0)

                                  {

                                        // spheres would be touch - change the direction

                                        Vec2f v1 = Vec2f(sph1->energyVector.x(), sph1->energyVector.z());

                                        Vec2f v2 = Vec2f(sph2->energyVector.x(), sph2->energyVector.z());

 

                                        getVelocitiesAfterCollision( v1, v2, Vec2f( sph1->center.x(), sph1->center.z()),

                                               Vec2f( sph2->center.x(), sph2->center.z()), sph1->radius + sph2->radius);     

 

                                        sph1->energyVector = Vec3f(v1.x(), 0, v1.y());

                                        sph2->energyVector = Vec3f(v2.x(), 0, v2.y());

 

                                        // slowdown by crash

                                        sph1->energyVector *= coefSlowDownByCrash;

                                        sph2->energyVector *= coefSlowDownByCrash;

 

 

                                                                          }

                           }

                    }

             }                  

       }

}

 

// collision sphere - plane

for (unsigned int i=0; i < scene->mSpheres.size(); i++)

{

       for (unsigned int j=0; j < scene->mSpheres[i]->primitive.size(); j++)

       {

             Sphere* sph = (Sphere*)(scene->mSpheres[i]->primitive[j]);

 

             // dont compute collision

             if (sph->crashInLastTime > 0)

                    continue;

 

             // check collision with every sphere

             for (unsigned int k = 0; k < scene->mObjects.size(); k++)

             {

                    for (unsigned int l = 0; l < scene->mObjects[k]->primitive.size(); l++)

                    {

                           InfinitePlane* pl = (InfinitePlane*)(scene->mObjects[k]->primitive[l]);

 

                           // caluculate the distance of sphere from plane

                           // d from equation of plane an+bn+cn+d=0

                           double d = - Dot( pl->origin, pl->normal);

 

                           // t of intersection plane with line from centre of sphere

                           double t = (- d - Dot(sph->center, pl->normal) ) / Dot(pl->normal,pl->normal);

 

                           Vec3f intersect = sph->center + t * (-pl->normal);

                            

                           double lengthEn = sph->energyVector.length();

 

                           intersectionSize = sph->radius - (sph->center - intersect).length();

                          

                           if (intersectionSize > 0)

                           {

                                  // sphere and plane are intersecting, doesnt look natural - not exactly but good enough for animation

                                  moveBackVector = sph->energyVector;

                                  Normalize(moveBackVector);

                                  sph->center = sph->center - intersectionSize * moveBackVector;

 

                                  // sphere would be touch the plane - change the direction

                                  sph->energyVector = pl->normal * 2 * (- sph->energyVector.normal().dot(pl->normal)) + sph->energyVector.normal();

                                  sph->energyVector = sph->energyVector.normal() * lengthEn;

                                  sph->energyVector *= coefSlowDownByCrash;

                           }

                    }

              }                  

       }

}

 

/*  Function compute new velocities of balls after their collision

    v1 - velocity - energy vector of the first ball

    v2 - velocity - energy vector of the second ball

    center1 - center of the first ball

    center2 - center of the second ball

    sumRadius - sum of radius of these two balls (suppose that they have same radius)

*/

void getVelocitiesAfterCollision(Vec2f &v1, Vec2f &v2, Vec2f center1, Vec2f center2, double sumRadius)

{     

       double gamaV = atan( (v1.y()-v2.y()) / (v1.x()-v2.x()) );

 

       double gamaXY = atan( (center2.y() - center1.y()) / (center2.x() - center1.x()));

       double d = sqrt((center2.x() - center1.x())*(center2.x() - center1.x()) + (center2.y() - center1.y())*(center2.y() - center1.y()));

       double alpha = asin( d * sin(gamaXY - gamaV) / sumRadius);

 

       double a = tan(gamaV + alpha);

 

       double delta = 2*(v1.x() - v2.x() + a *(v1.y()-v2.y())) / ((1 + a*a)*2);

 

       // new energies of balls after collision

       Vec2f newV2 = Vec2f(v2.x() + delta, v2.y() + a*delta);

       Vec2f newV1 = Vec2f(v1.x() - delta, v1.y() - a*delta);

 

       v1 = newV1;

       v2 = newV2; 

}

 

// from Sphere.hxx

// Method rotated sphere in actual position in direction (energyX, energyY) -> (energyX,0,energyY)

void Rotate(double energyX, double energyY)

{

       double energySize = sqrt(energyX * energyX + energyY * energyY);

 

       if (energySize < Epsilon)

       {

             // only accumulation error :-/

             return;

       }

 

       double spherePerimeter = 2 * M_PI * radius;

 

       // compute angle ball rolled by (from 0 to 1)

       // minus is for correct rolling direction :-)

       double fi = energySize / spherePerimeter;

       double sinFi = sin(M_PI * fi);

       double cosFi = cos(M_PI * fi);

 

       // move values to unit circle

       Vec2f pom(energyX, energyY);

       Normalize(pom);

 

       //double cosPsi = - pom.x();

       //double sinPsi = pom.y();

 

       double cosPsi = pom.x();

       double sinPsi = pom.y();

 

       // compute new northpole using polar coords

       // Vec3f newNorth(cosFi * sinPsi, cosFi, sinFi * cosPsi);

       Vec3f newNorth(cosPsi, 0, sinPsi);

       Vec3f rotationAxis = Cross(newNorth, Vec3f(0,1,0));

       Normalize(rotationAxis);

 

       double x = rotationAxis.x();

       double y = rotationAxis.y();

       double z = rotationAxis.z();

 

       Vec3f Mx(1 + (1-cosFi)*(x*x-1),z*sinFi+(1-cosFi)*x*y,-y*sinFi+(1-cosFi)*x*z);

       Vec3f My(-z*sinFi+(1-cosFi)*x*y,1 + (1-cosFi)*(y*y-1),x*sinFi+(1-cosFi)*y*z);

       Vec3f Mz(y*sinFi+(1-cosFi)*x*z, -x*sinFi+(1-cosFi)*y*z, 1 + (1-cosFi)*(z*z-1));

 

       // Rotation matrix multiplication...

       Vec3f newrotX (

             rotX.x() * Mx.x() + rotY.x() * Mx.y() + rotZ.x() * Mx.z(),

             rotX.y() * Mx.x() + rotY.y() * Mx.y() + rotZ.y() * Mx.z(),

             rotX.z() * Mx.x() + rotY.z() * Mx.y() + rotZ.z() * Mx.z()

             );

 

       Vec3f newrotY (

             rotX.x() * My.x() + rotY.x() * My.y() + rotZ.x() * My.z(),

             rotX.y() * My.x() + rotY.y() * My.y() + rotZ.y() * My.z(),

             rotX.z() * My.x() + rotY.z() * My.y() + rotZ.z() * My.z()

             );

 

       Vec3f newrotZ (

             rotX.x() * Mz.x() + rotY.x() * Mz.y() + rotZ.x() * Mz.z(),

             rotX.y() * Mz.x() + rotY.y() * Mz.y() + rotZ.y() * Mz.z(),

             rotX.z() * Mz.x() + rotY.z() * Mz.y() + rotZ.z() * Mz.z()

             );

 

       rotX = newrotX;

       rotY = newrotY;

       rotZ = newrotZ;

}

 

References: