void particleCollision(ParticleGeometryCollisionEventDetails* const details)
{
    ParticleSystemRefPtr TheSystem= details->getSystem();
    UInt32 ParticleIndex(details->getParticleIndex());


    Real32 phi= osgACos((-TheSystem->getVelocity(ParticleIndex).dot(details->getHitNormal()))/(TheSystem->getVelocity(ParticleIndex).length()*details->getHitNormal().length()));

    if( phi < osgDegree2Rad(80.0) )
    {
        TheSystem->killParticle(ParticleIndex);
    }
    else
    {
        //Reflect the Particle
        Vec3f Reflect(TheSystem->getVelocity(ParticleIndex).reflect(details->getHitNormal()));
        TheSystem->setVelocity(Reflect, ParticleIndex);
        TheSystem->setPosition(details->getHitPoint() + (0.00001f*Reflect), ParticleIndex);
    }

}
/*! Calculates the trapezoidal transformation matrix \a matNT that transforms
    post projection light space so that shadow map resolution in the
    "foreground" is maximized.
    The major steps are:
      - compute the intersection of eyeFrust and lightFrust
      - construct a trapezoid that contains the intersection
      - determine the transformation that maps this trapezoid to the
        (-1, 1) square

    Returns \c true if the transform was computed, \c false otherwise (e.g. if
    the intersection of eyeFrust and lightFrust is empty).
    
    For details see "T. Martin, T.-S. Tan: Anti-aliasing and Continuity
                     with Trapezoidal Shadow Maps" 
 */
bool TrapezoidalShadowMapEngine::calcTrapezoidalTransform(
          Matrixr       &matNT,
    const Matrixr       &matEyeToWorld, 
    const Matrixr       &matLightFull,
    const FrustumVolume &eyeFrust,
    const FrustumVolume &lightFrust    )
{
    // obtain post proj. light space eye position
    Pnt3r eyePos;
    matEyeToWorld.mult    (eyePos, eyePos);
    matLightFull .multFull(eyePos, eyePos);

    // intersect eye and light frusta, get vertices and center of intersection
    std::vector<Pnt3r> intVerts;
    Pnt3r              intCenter;
    intersectFrusta(eyeFrust, lightFrust, intVerts, intCenter);

    if(intVerts.empty() == true)
        return false;

    // xform intCenter and intVerts to post proj. light space
    matLightFull.multFull(intCenter, intCenter);

    std::vector<Pnt3r>::iterator ivIt  = intVerts.begin();
    std::vector<Pnt3r>::iterator ivEnd = intVerts.end  ();

    for(; ivIt != ivEnd; ++ivIt)
        matLightFull.multFull(*ivIt, *ivIt);
    
    Pnt2r eyePos2D   (eyePos   [0], eyePos   [1]);
    Pnt2r intCenter2D(intCenter[0], intCenter[1]);

    // center line, normal, direction and distance from origin
    Vec2r clDir (intCenter2D - eyePos2D);
    clDir.normalize();
    Vec2r clNorm(-clDir[1], clDir[0]);

    // distance of the center line from the origin
    Real clDist = clNorm.dot(eyePos2D.subZero());

    // compute top and base lines:
    //  - project intVerts onto the center line.
    //  - top line is perpendicular to center line and goes through the
    //    projected point closest to eyePos
    //  - base line is perpendicular to center line and goes through the
    //    projected point farthest from eyePos

    Pnt2r tlBase;
    Pnt2r blBase;
    Real  topDist  = TypeTraits<Real>::getMax();
    Real  baseDist = TypeTraits<Real>::getMin();

    std::vector<Pnt3r>::const_iterator ivCIt  = intVerts.begin();
    std::vector<Pnt3r>::const_iterator ivCEnd = intVerts.end  ();

    for(; ivCIt != ivCEnd; ++ivCIt)
    {
        Pnt2r ivPnt((*ivCIt)[0], (*ivCIt)[1]);
        
        ivPnt = ivPnt - (clNorm.dot(ivPnt) - clDist) * clNorm;

        Real dist = (ivPnt - eyePos2D).squareLength();
        dist *= osgSgn(clDir.dot(ivPnt - eyePos2D));

        if(dist < topDist)
        {
            topDist = dist;
            tlBase  = ivPnt;
        }
        
        if(dist > baseDist)
        {
            baseDist = dist;
            blBase   = ivPnt;
        }
    }

    topDist  = osgSgn(topDist ) * osgSqrt(osgAbs(topDist ));
    baseDist = osgSgn(baseDist) * osgSqrt(osgAbs(baseDist));

    // compute side lines:
    //  - choose focusPnt (everything closer to the near plane is mapped to
    //    80% of the shadow map) - here we just take the point at 0.7 between
    //    tlBase and blBase
    //  - find a point (trapTip, q in the paper) on center line such that
    //    focusPnt is mapped the 80% line in the shadow map
    //  - choose lines through q that touch the convex hull of intVerts

    ivCIt  = intVerts.begin();
    ivCEnd = intVerts.end  ();

//    Real  centerDist = (intCenter2D - eyePos2D).length();

    Real  lambda     = baseDist   - topDist;
    Real  delta      = 0.5f * lambda;
    Real  xi         = -0.6f;
    Real  eta        = ((lambda * delta) + (lambda * delta * xi)) /
                       (lambda - 2.f * delta - lambda * xi      );
    Pnt2r trapTip    = tlBase - (eta   * clDir);
    Pnt2r focusPnt   = tlBase + (delta * clDir);

    // on both sides of the center line, find the point in intVerts that has
    // the smallest |cosine| (largest angle) between clDir and the vector
    // from trapTip to intVerts[i]
    Pnt2r posPnt;
    Real  posCos = 1.f;
    Pnt2r negPnt;
    Real  negCos = 1.f;

    for(UInt32 i = 0; ivCIt != ivCEnd; ++ivCIt, ++i)
    {
        Pnt2r ivPnt((*ivCIt)[0], (*ivCIt)[1]);
   
        Vec2r v       = ivPnt - trapTip;
        v.normalize();
        Real  currCos = osgAbs(clDir.dot(v));

        if(clNorm.dot(v) >= 0.f)
        {
            if(currCos <= posCos)
            {
                posPnt = ivPnt;
                posCos = currCos;
            }
        }
        else
        {
            if(currCos <= negCos)
            {
                negPnt = ivPnt;
                negCos = currCos;
            }
        }
    }

    // compute corners of trapezoid:
    Pnt2r trapVerts [4];
    Pnt2r extraVerts[2];
    Real  posTan = osgTan(osgACos(posCos));
    Real  negTan = osgTan(osgACos(negCos));

    trapVerts[0] = blBase - ((eta + lambda) * negTan * clNorm);
    trapVerts[1] = blBase + ((eta + lambda) * posTan * clNorm);
    trapVerts[2] = tlBase + ( eta           * posTan * clNorm);
    trapVerts[3] = tlBase - ( eta           * negTan * clNorm);

    extraVerts[0] = focusPnt + ((eta + delta) * posTan * clNorm);
    extraVerts[1] = focusPnt - ((eta + delta) * negTan * clNorm);

    // == xform trapezoid to unit square ==

    // M1 = R * T1  -- translate center of top line to origin and rotate
    Vec2r u = 0.5f * (trapVerts[2].subZero() + trapVerts[3].subZero());
    Vec2r v =         trapVerts[3]           - trapVerts[2];
    v.normalize();

    matNT.setValue( v[0],  v[1], 0.f, -(u[0] * v[0] + u[1] * v[1]),
                   -v[1],  v[0], 0.f,  (u[0] * v[1] - u[1] * v[0]),
                    0.f,   0.f,  1.f,  0.f,
                    0.f,   0.f,  0.f,  1.f);

    // M2 = T2 * M1  -- translate tip to origin
    matNT[3][0] = - (matNT[0][0] * trapTip[0] + matNT[1][0] * trapTip[1]);
    matNT[3][1] = - (matNT[0][1] * trapTip[0] + matNT[1][1] * trapTip[1]);

    // M3 = H * M2  -- shear to make it symmetric wrt to the y axis
    //    v = M2 * u
    v[0] = matNT[0][0] * u[0] + matNT[1][0] * u[1] + matNT[3][0];
    v[1] = matNT[0][1] * u[0] + matNT[1][1] * u[1] + matNT[3][1];
  
    Real a = - v[0] / v[1];
    
    //    matNT[*][0] : = mat[*][0] + a * mat[*][1]
    matNT[0][0] += a * matNT[0][1];
    matNT[1][0] += a * matNT[1][1];
    matNT[2][0] += a * matNT[2][1];
    matNT[3][0] += a * matNT[3][1];

    // M4 = S1 * M3  -- scale to make sidelines orthogonal and 
    //                  top line is at y == 1
    //    v = 1 / (M3 * t2)
    v[0] = 1.f / (matNT[0][0] * trapVerts[2][0] + matNT[1][0] * trapVerts[2][1] + matNT[3][0]);
    v[1] = 1.f / (matNT[0][1] * trapVerts[2][0] + matNT[1][1] * trapVerts[2][1] + matNT[3][1]);

    matNT[0][0] *= v[0];    matNT[0][1] *= v[1];
    matNT[1][0] *= v[0];    matNT[1][1] *= v[1];
    matNT[2][0] *= v[0];    matNT[2][1] *= v[1];
    matNT[3][0] *= v[0];    matNT[3][1] *= v[1];
    
    // M5 = N * M4  -- turn trapezoid into rectangle
    matNT[0][3] = matNT[0][1];
    matNT[1][3] = matNT[1][1];
    matNT[2][3] = matNT[2][1];
    matNT[3][3] = matNT[3][1];
    matNT[3][1] += 1.f;

    // M6 = T3 * M5  -- translate center to origin
    //    u = "M5 * t0"  - only y and w coordinates
    //    v = "M5 * t2"  - only y and w coordinates
    u[0] = matNT[0][1] * trapVerts[0][0] + matNT[1][1] * trapVerts[0][1] + matNT[3][1];
    u[1] = matNT[0][3] * trapVerts[0][0] + matNT[1][3] * trapVerts[0][1] + matNT[3][3];
    v[0] = matNT[0][1] * trapVerts[2][0] + matNT[1][1] * trapVerts[2][1] + matNT[3][1];
    v[1] = matNT[0][3] * trapVerts[2][0] + matNT[1][3] * trapVerts[2][1] + matNT[3][3];
    a    = - 0.5f * (u[0] / u[1] + v[0] / v[1]);

    matNT[0][1] += matNT[0][3] * a;
    matNT[1][1] += matNT[1][3] * a;
    matNT[2][1] += matNT[2][3] * a;
    matNT[3][1] += matNT[3][3] * a;

    // M7 = S2 * M6  -- scale to fill -1/+1 square
    //    u = "M6 * t0"  - only y and w coordinates
    u[0] = matNT[0][1] * trapVerts[0][0] + matNT[1][1] * trapVerts[0][1] + matNT[3][1];
    u[1] = matNT[0][3] * trapVerts[0][0] + matNT[1][3] * trapVerts[0][1] + matNT[3][3];
    a    = -u[1] / u[0];

    matNT[0][1] *= a;
    matNT[1][1] *= a;
    matNT[2][1] *= a;
    matNT[3][1] *= a;

    return true;
}