Real32 FrustumVolume::getScalarVolume() const
{
    const Int32 faces[6][4] =
    {
        {0,1,3,2},
        {4,5,7,6},
        {0,4,5,1},
        {2,6,7,3},
        {2,6,4,0},
        {1,5,7,3}
    };

    Pnt3f vertices[8];
    Line  lines   [4];

    _planeVec[5].intersect(_planeVec[3], lines[3]); 
    _planeVec[3].intersect(_planeVec[4], lines[2]); 
    _planeVec[4].intersect(_planeVec[2], lines[0]); 
    _planeVec[2].intersect(_planeVec[5], lines[1]); 
    
    for(Int32 i = 0; i < 4; i++)
    {
        _planeVec[0].intersectInfinite(lines[i], vertices[    i]);
        _planeVec[1].intersectInfinite(lines[i], vertices[4 + i]);
    }
    

    Pnt3f center = Pnt3f(0.f, 0.f, 0.f);

    for(Int32 i = 0; i < 8; i++)
    {
        center = center + vertices[i].subZero();
    }

    center /= 8.f;
    
    Real32 volume = .0f;

    for(Int32 i = 0; i < 6; i++)
    {
        Real32 height;
        Real32 area;

        height = 
            _planeVec[i].getNormal().dot(center) - 
            _planeVec[i].getDistanceFromOrigin();
        
        Vec3f main_diag = vertices[faces[i][0]] - vertices[faces[i][2]];
        Vec3f sec_diag  = vertices[faces[i][1]] - vertices[faces[i][3]];

        area = osgAbs((main_diag.cross(sec_diag)).length() / 2.f);        

        volume += osgAbs((height*area)) / 3.f;
    }

    return volume;
}
 virtual void update(const UpdateEventUnrecPtr e)
 {
     ForceOnCharacter.setValues(0.0,0.0,0.0);
     Real32 PushForce(10000.0);
     Real32 Speed(20.0);
     if(_IsUpKeyDown)
     {
         ForceOnCharacter += Vec3f(0.0, PushForce, 0.0);
     }
     if(_IsDownKeyDown)
     {
         ForceOnCharacter += Vec3f(0.0, -PushForce, 0.0);
     }
     if(_IsLeftKeyDown)
     {
         ForceOnCharacter += Vec3f(-PushForce, 0.0, 0.0);
     }
     if(_IsRightKeyDown)
     {
         ForceOnCharacter += Vec3f(PushForce, 0.0, 0.0);
     }
     if(ForceOnCharacter != Vec3f(0.0,0.0,0.0))
     {
         ShipBody->setEnable(true);
     }
         if(ForceOnCharacter.x() !=0.0)
         {
             ShipMotor->setFMax(osgAbs(ForceOnCharacter.x()));
             ShipMotor->setVel(osgSgn(ForceOnCharacter.x())*Speed);
         }
         else
         {
             ShipMotor->setFMax(0.0);
             ShipMotor->setVel(0.0);
         }
         if(ForceOnCharacter.y() !=0.0)
         {
             ShipMotor->setFMax2(osgAbs(ForceOnCharacter.y()));
             ShipMotor->setVel2(osgSgn(ForceOnCharacter.y())*Speed);
         }
         else
         {
             ShipMotor->setFMax2(0.0);
             ShipMotor->setVel2(0.0);
         }
         if(ForceOnCharacter.z() !=0.0)
         {
             ShipMotor->setFMax3(osgAbs(ForceOnCharacter.z()));
             ShipMotor->setVel3(osgSgn(ForceOnCharacter.z())*Speed);
         }
         else
         {
             ShipMotor->setFMax3(0.0);
             ShipMotor->setVel3(0.0);
         }
 }
Real32 OctreeAStarAlgorithm::manhattanDistanceCost(Octree::OTNodePtr node,
                                                   const Pnt3f& Location,
                                                   Real32 CostPerUnit)
{
    Pnt3f Target;
    node->getVolume().getCenter(Target);
    return CostPerUnit * (osgAbs(Location.x() - Target.x())
                        + osgAbs(Location.y() - Target.y())
                        + osgAbs(Location.z() - Target.z()));
}
Real32 DirectShowVideoWrapper::getAudioVolume(void) const
{
    if(isInitialized())
    {
		HRESULT hr;
        TCHAR szErr[MAX_ERROR_TEXT_LEN];

		CComPtr<IBasicAudio> basicAudio;
		hr = _pGraphBuilder->QueryInterface(&basicAudio);
		if (FAILED(hr))
        {
            AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
            SWARNING << "Unable to get IBasicAudio, error: " << szErr << std::endl;
			return 0.0;
		}

		long Result;
		hr = basicAudio->get_Volume(&Result);
		if (FAILED(hr))
        {
            AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
            SWARNING << "Unable to get audio volume, error: " << szErr << std::endl;
			return 0.0;
		}

		return 1.0f - (osgAbs(static_cast<Real32>(Result))/10000.0f);
	}
	return 0.0;
}
void ShaderShadowMapEngine::calcPointLightRange(
    const PointLight *pointL,      Real32  lightThreshold,
          Real32      defaultNear, Real32  defaultFar,
          Real32     &outNear,     Real32 &outFar         )
{
    outNear = defaultNear;
    outFar  = defaultFar;

    Real32 kQ = pointL->getQuadraticAttenuation();
    Real32 kL = pointL->getLinearAttenuation   ();
    Real32 kC = pointL->getConstantAttenuation ();

    if(osgAbs(kQ) > TypeTraits<Real32>::getDefaultEps())
    {
        Real32 det = kL * kL  - 4.f * kQ * (kC - 1.f / lightThreshold);

        if(det >= 0)
        {
            det       = osgSqrt(det);
            Real32 r1 = - kL + det / (2.f * kQ);
            Real32 r2 = - kL - det / (2.f * kQ);

            if(r1 > 0.f && r2 > 0.f)
            {
                outFar = osgMin(r1, r2);
            }
            else if(r1 > 0.f)
            {
                outFar = r1;
            }
            else if(r2 > 0.f)
            {
                outFar = r2;
            }
        }
    }
    else if(osgAbs(kL) > TypeTraits<Real32>::getDefaultEps())
    {
        Real32 r = (1.f / lightThreshold - kC) / kL;

        if(r > 0.f)
        {
            outFar = r;
        }
    }
}
OSG_BASE_DLLMAPPING bool MatrixPerspective(OSG::Matrixr &result,
                                           OSG::Real     rFovy,
                                           OSG::Real     rAspect,
                                           OSG::Real     rNear,
                                           OSG::Real     rFar)
{
    Real ct    = osgTan(rFovy);
    bool error = false;

    if(rNear > rFar)
    {
        SWARNING << "MatrixPerspective: near " << rNear << " > far " << rFar
                 << "!\n" << std::endl;

        error = true;
    }

    if(rFovy <= TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: fovy " << rFovy << " very small!\n"
                 << std::endl;

        error = true;
    }

    if(osgAbs(rNear - rFar) < TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: near " << rNear << " ~= far " << rFar
                 << "!\n" << std::endl;

        error = true;
    }

    if(rAspect < TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: aspect ratio " << rAspect
                 << " very small!\n" << std::endl;

        error = true;
    }

    if(error)
    {
        result.setIdentity();
        return true;
    }

    MatrixFrustum( result, 
                  -rNear * ct * rAspect, 
                   rNear * ct * rAspect,
                  -rNear * ct, 
                   rNear * ct, 
                   rNear, 
                   rFar                );

    return false;
}
bool SphereVolume::isOnSurface (const Pnt3f &point) const
{
    if(osgAbs((point - _center).length() - _radius) <
       TypeTraits<Real32>::getDefaultEps())
    {
        return true;
    }
    else
    {
        return false;
    }
}
void FrameHandler::frame(Time frameTime)
{
    if(_mfUninitializedFrameTasks.size() != 0)
    {
        this->init();
    }

    setCurrTime(frameTime);

    if(osgAbs(_sfStartTime.getValue()) < 0.00001)
    {
        setStartTime(_sfCurrTime.getValue());

        setLastTime(0.f);
    }

    _sfCurrTime.getValue() -= _sfStartTime.getValue();

    if(_sfPaused.getValue() == false)
    {
        SFTime *pSFTimeStamp = editSFTimeStamp();

        if(_sfConstantTime.getValue() == true)
        {
            pSFTimeStamp->getValue() += _sfConstantTimeStep.getValue();

            if(pSFTimeStamp->getValue() < 0.)
                pSFTimeStamp->setValue(0.0);
        }
        else
        {
            pSFTimeStamp->getValue() +=
                (_sfCurrTime.getValue() - _sfLastTime.getValue()) *
                _sfTimeScale.getValue();

            if(pSFTimeStamp->getValue() < 0.)
                pSFTimeStamp->setValue(0.0);
        }
    }

    setLastTime(_sfCurrTime.getValue());

    ++(editSFFrameCount()->getValue());

    callTasks();
}
/*! 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;
}
void handleUpdate(UpdateEventDetails* const details,
                  PhysicsBody* const CharacterPhysicsBody,
                  PhysicsLMotorJoint* const CharacterMover)
{
    ForceOnCharacter.setValues(0.0,0.0,0.0);
    Real32 PushForce(55000.0);
    Real32 Speed(10.0);
    if(_IsUpKeyDown)
    {
        ForceOnCharacter += Vec3f(0.0, PushForce, 0.0);
    }
    if(_IsDownKeyDown)
    {
        ForceOnCharacter += Vec3f(0.0, -PushForce, 0.0);
    }
    if(_IsLeftKeyDown)
    {
        ForceOnCharacter += Vec3f(-PushForce, 0.0, 0.0);
    }
    if(_IsRightKeyDown)
    {
        ForceOnCharacter += Vec3f(PushForce, 0.0, 0.0);
    }
    if(_ShouldJump)
    {
        ForceOnCharacter += Vec3f(0.0, 0.0, 50000.0);
        _ShouldJump = false;
    }
    if(ForceOnCharacter != Vec3f(0.0,0.0,0.0))
    {
        CharacterPhysicsBody->setEnable(true);
    }
    if(ForceOnCharacter.x() !=0.0)
    {
        CharacterMover->setFMax(osgAbs(ForceOnCharacter.x()));
        CharacterMover->setVel(osgSgn(ForceOnCharacter.x())*Speed);
    }
    else
    {
        CharacterMover->setFMax(0.0);
        CharacterMover->setVel(0.0);
    }
    if(ForceOnCharacter.y() !=0.0)
    {
        CharacterMover->setFMax2(osgAbs(ForceOnCharacter.y()));
        CharacterMover->setVel2(osgSgn(ForceOnCharacter.y())*Speed);
    }
    else
    {
        CharacterMover->setFMax2(0.0);
        CharacterMover->setVel2(0.0);
    }
    if(ForceOnCharacter.z() !=0.0)
    {
        CharacterMover->setFMax3(osgAbs(ForceOnCharacter.z()));
        CharacterMover->setVel3(osgSgn(ForceOnCharacter.z())*Speed);
    }
    else
    {
        CharacterMover->setFMax3(0.0);
        CharacterMover->setVel3(0.0);
    }

    Real32 RotationRate(1.57);
    if(_IsAKeyDown)
    {
        Quaternion newRotation(CharacterPhysicsBody->getQuaternion());
        newRotation.mult(Quaternion(Vec3f(0.0,0.0,1.0),RotationRate*details->getElapsedTime()));
        CharacterPhysicsBody->setQuaternion( newRotation );
    }
    if(_IsDKeyDown)
    {
        Quaternion newRotation(CharacterPhysicsBody->getQuaternion());
        newRotation.mult(Quaternion(Vec3f(0.0,0.0,1.0),-RotationRate*details->getElapsedTime()));
        CharacterPhysicsBody->setQuaternion( newRotation );
    }
}
void
OgreMeshReader::verifyBoneAssignment(VertexElementStore &vertexElements,
                                     Int16              &boneIdxVE,
                                     Int16              &boneWeightVE   )
{
    OSG_OGRE_LOG(("OgreMeshReader::verifyBoneAssignment\n"));

    GeoVec4fPropertyUnrecPtr boneIdxProp;
    GeoVec4fPropertyUnrecPtr boneWeightProp;

    if(boneIdxVE < 0)
    {
        SWARNING << "OgreMeshReader::verifyBoneAssignment: "
                 << "Invalid bone index vertex element"
                 << std::endl;
        return;
    }

    if(boneWeightVE < 0)
    {
        SWARNING << "OgreMeshReader::verifyBoneAssignment: "
                 << "Invalid bone weight vertex element"
                 << std::endl;
        return;
    }

    boneIdxProp    = dynamic_pointer_cast<GeoVec4fProperty>(vertexElements[boneIdxVE   ].prop);
    boneWeightProp = dynamic_pointer_cast<GeoVec4fProperty>(vertexElements[boneWeightVE].prop);

    GeoVec4fProperty::StoredFieldType* boneIdxF    = boneIdxProp   ->editFieldPtr();
    GeoVec4fProperty::StoredFieldType* boneWeightF = boneWeightProp->editFieldPtr();

    if(boneIdxF->size() != boneWeightF->size())
    {
        SWARNING << "OgreMeshReader::verifyBoneAssignment: "
                 << "Size mismatch between bone indices and weights."
                 << std::endl;
        return;
    }

    for(UInt32 vertIdx = 0; vertIdx < boneIdxF->size(); ++vertIdx)
    {
        Real32 sumWeights = 0.f;
        Real32 numWeights = 0.f;

        for(UInt16 i = 0; i < 4; ++i)
        {
            if((*boneIdxF)[vertIdx][i] >= 0.f)
            {
                sumWeights += (*boneWeightF)[vertIdx][i];
                numWeights += 1.f;
            }
            else
            {
                break;
            }
        }

        if(osgAbs(sumWeights - 1.f) > 1e-6f)
        {
            SWARNING << "OgreMeshReader::verifyBoneAssignment: "
                     << "Vertex '" << vertIdx << "' influences not normalized,"
                     << " sumWeights " << sumWeights
                     << " numWeights " << numWeights
                     << std::endl;
        }
    }
}
Vec3f CylinderDistribution3D::generate(void) const
{
    Vec3f Result;

    switch(getSurfaceOrVolume())
    {
    case SURFACE:
        {
            std::vector<Real32> Areas;
            //Min Cap
            Areas.push_back(0.5*osgAbs(getMaxTheta() - getMinTheta())*(getOuterRadius()*getOuterRadius() - getInnerRadius()*getInnerRadius()));
            //Max Cap
            Areas.push_back(Areas.back() + 0.5*osgAbs(getMaxTheta() - getMinTheta())*(getOuterRadius()*getOuterRadius() - getInnerRadius()*getInnerRadius()));
            //Inner Tube
            Areas.push_back(Areas.back() + getInnerRadius()*osgAbs(getMaxTheta() - getMinTheta()) * getHeight());
            //Outer Tube
            Areas.push_back(Areas.back() + getOuterRadius()*osgAbs(getMaxTheta() - getMinTheta()) * getHeight());

            bool HasTubeSides(osgAbs(getMaxTheta() - getMinTheta()) - 6.283185 < -0.000001);
            if(HasTubeSides)
            {
                //MinTheta Tube Side
                Areas.push_back(Areas.back() + (getOuterRadius() - getInnerRadius()) * getHeight());

                //MaxTheta Tube Side
                Areas.push_back(Areas.back() + (getOuterRadius() - getInnerRadius()) * getHeight());
            }

            Real32 PickEdge(RandomPoolManager::getRandomReal32(0.0,1.0));
            if(PickEdge < Areas[0]/Areas.back())
            {
                //Max Cap
                Real32 Temp(osgSqrt(RandomPoolManager::getRandomReal32(0.0,1.0)));
                Real32 Radius(getInnerRadius() + Temp*(getOuterRadius() - getInnerRadius()));
                Real32 Theta( RandomPoolManager::getRandomReal32(getMinTheta(),getMaxTheta()) );
                Result = getCenter().subZero()
                    + (Radius*osgSin(Theta))*getTangent()
                    + (Radius*osgCos(Theta))*getBinormal()
                    + (getHeight()/static_cast<Real32>(2.0))*getNormal();
            }
            else if(PickEdge < Areas[1]/Areas.back())
            {
                //Min Cap
                Real32 Temp(osgSqrt(RandomPoolManager::getRandomReal32(0.0,1.0)));
                Real32 Radius(getInnerRadius() + Temp*(getOuterRadius() - getInnerRadius()));
                Real32 Theta( RandomPoolManager::getRandomReal32(getMinTheta(),getMaxTheta()) );
                Result = getCenter().subZero()
                    + (Radius*osgSin(Theta))*getTangent()
                    + (Radius*osgCos(Theta))*getBinormal()
                    + (-getHeight()/static_cast<Real32>(2.0))*getNormal();
            }
            else if(PickEdge < Areas[2]/Areas.back())
            {
                //Inner Tube
                Real32 Theta( RandomPoolManager::getRandomReal32(getMinTheta(),getMaxTheta()) );
                Real32 Height(RandomPoolManager::getRandomReal32(-getHeight()/2.0,getHeight()/2.0));
                Result =  getCenter().subZero()
                    + getInnerRadius()*osgSin(Theta)*getTangent()
                    + getInnerRadius()*osgCos(Theta)*getBinormal()
                    + Height*getNormal();
            }
            else if(PickEdge < Areas[3]/Areas.back())
            {
                //Outer Tube
                Real32 Theta( RandomPoolManager::getRandomReal32(getMinTheta(),getMaxTheta()) );
                Real32 Height(RandomPoolManager::getRandomReal32(-getHeight()/2.0,getHeight()/2.0));
                Result =  getCenter().subZero()
                    + getOuterRadius()*osgSin(Theta)*getTangent()
                    + getOuterRadius()*osgCos(Theta)*getBinormal()
                    + Height*getNormal();
            }
            else if(HasTubeSides && PickEdge < Areas[4]/Areas.back())
            {
                //MinTheta Tube Side
                Real32 Temp(osgSqrt(RandomPoolManager::getRandomReal32(0.0,1.0)));
                Real32 Radius(getInnerRadius() + Temp*(getOuterRadius() - getInnerRadius()));
                Real32 Height(RandomPoolManager::getRandomReal32(-getHeight()/2.0,getHeight()/2.0));
                Result = getCenter().subZero()
                    + (Radius*osgSin(getMinTheta()))*getTangent()
                    + (Radius*osgCos(getMinTheta()))*getBinormal()
                    + Height*getNormal();
            }
            else if(HasTubeSides && PickEdge < Areas[5]/Areas.back())
            {
                //MaxTheta Tube Side
                Real32 Temp(osgSqrt(RandomPoolManager::getRandomReal32(0.0,1.0)));
                Real32 Radius(getInnerRadius() + Temp*(getOuterRadius() - getInnerRadius()));
                Real32 Height(RandomPoolManager::getRandomReal32(-getHeight()/2.0,getHeight()/2.0));
                Result = getCenter().subZero()
                    + (Radius*osgSin(getMaxTheta()))*getTangent()
                    + (Radius*osgCos(getMaxTheta()))*getBinormal()
                    + Height*getNormal();
            }
            else
            {
                assert(false && "Should never reach this point");
            }
            break;
        }
    case VOLUME:
    default:
        {
            //To get a uniform distribution across the disc get a uniformly distributed allong 0.0 - 1.0
            //Then Take the square root of that.  This gives a square root distribution from 0.0 - 1.0
            //This square root distribution is used for the random radius because the area of a disc is 
            //dependant on the square of the radius, i.e it is a quadratic function
            Real32 Temp(osgSqrt(RandomPoolManager::getRandomReal32(0.0,1.0)));
            Real32 Radius(getInnerRadius() + Temp*(getOuterRadius() - getInnerRadius()));
            Real32 Height(RandomPoolManager::getRandomReal32(-getHeight()/2.0,getHeight()/2.0));
            Real32 Theta( RandomPoolManager::getRandomReal32(getMinTheta(),getMaxTheta()) );
            Result = getCenter().subZero()
                   + (Radius*osgSin(Theta))*getTangent()
                   + (Radius*osgCos(Theta))*getBinormal()
                   + Height*getNormal();
            break;
        }
    }

    return Result;
}
OSG_BASE_DLLMAPPING bool MatrixStereoPerspective(OSG::Matrixr &projection,
                                                 OSG::Matrixr &projtrans,
                                                 OSG::Real     rFovy,
                                                 OSG::Real     rAspect,
                                                 OSG::Real     rNear,
                                                 OSG::Real     rFar,
                                                 OSG::Real     rZeroparallax,
                                                 OSG::Real     rEyedistance,
                                                 OSG::Real     rWhicheye,
                                                 OSG::Real     rOverlap)
{
    Real rLeft;
    Real rRight;
    Real rTop;
    Real rBottom;

    Real gltan;
    Real rEye = -rEyedistance * (rWhicheye - .5f);
    Real d;

    bool error = false;

    if(rNear > rFar)
    {
        SWARNING << "MatrixPerspective: near " << rNear << " > far " << rFar
                 << "!\n" << std::endl;

        error = true;
    }

    if(rFovy <= TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: fovy " << rFovy << " very small!\n"
                 << std::endl;

        error = true;
    }

    if(osgAbs(rNear - rFar) < TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: near " << rNear << " ~= far " << rFar
                 << "!\n" << std::endl;

        error = true;
    }

    if(rAspect < TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: aspect ratio " << rAspect
                 << " very small!\n"                   << std::endl;

        error = true;
    }

    if(rZeroparallax < TypeTraits<Real>::getDefaultEps())
    {
        SWARNING << "MatrixPerspective: zero parallax " << rZeroparallax
                 << " very small, setting to 1!\n"      << std::endl;

        rZeroparallax = 1.f;
        error         = true;
    }
    
    if(error)
    {
        projection.setIdentity();
        projtrans .setIdentity();

        return true;
    }

    /* Calculate upper and lower clipping planes */
    rTop    = osgTan(rFovy / 2.0f) * rNear; 
    rBottom = -rTop;

    /* Calculate left and right clipping planes */
    gltan  = osgTan(rFovy / 2.0f) * rAspect;  

    rLeft  = (-gltan + rEye / rZeroparallax) * rNear;
    rRight = ( gltan + rEye / rZeroparallax) * rNear;

    d = rRight - rLeft;

    rLeft  += d * (1.f - rOverlap) * (rWhicheye - .5f);
    rRight += d * (1.f - rOverlap) * (rWhicheye - .5f);

    MatrixFrustum(projection, rLeft, rRight, rBottom, rTop, rNear, rFar);

    projtrans.setIdentity();

    projtrans[3][0] = rEye;

    return false;
}
Real32 dist3D_Segment_to_Segment(const Pnt3f& s1p, 
                                 const Vec3f& s1d,
                                 const Pnt3f& s2p, 
                                 const Vec3f& s2d)
{
    const float  SMALL_NUM = 1e-9f; // anything that avoids division overflow
    const Vec3f& u = s1d;
    const Vec3f& v = s2d;
          Vec3f  w = s1p - s2p;

    Real32    a = u.dot(u);        // always >= 0
    Real32    b = u.dot(v);
    Real32    c = v.dot(v);        // always >= 0
    Real32    d = u.dot(w);
    Real32    e = v.dot(w);
    Real32    D = a*c - b*b;       // always >= 0
    Real32    sc, sN, sD = D;      // sc = sN / sD, default sD = D >= 0
    Real32    tc, tN, tD = D;      // tc = tN / tD, default tD = D >= 0

    // compute the line parameters of the two closest points
    if(D < SMALL_NUM)   // the lines are almost parallel
    {
        sN = 0.0f;        // force using point P0 on segment S1
        sD = 1.0f;        // to prevent possible division by 0.0 later
        tN = e;
        tD = c;
    }
    else                 // get the closest points on the infinite lines
    {
        sN = (b*e - c*d);
        tN = (a*e - b*d);

        if(sN < 0.0f)       // sc < 0 => the s=0 edge is visible
        {
            sN = 0.0f;
            tN = e;
            tD = c;
        }
        else if (sN > sD)   // sc > 1 => the s=1 edge is visible
        {
            sN = sD;
            tN = e + b;
            tD = c;
        }
    }

    if(tN < 0.0f)            // tc < 0 => the t=0 edge is visible
    {
        tN = 0.0f;
        // recompute sc for this edge
        if(-d < 0.0f)
        {
            sN = 0.0f;
        }
        else if(-d > a)
        {
            sN = sD;
        }
        else 
        {
            sN = -d;
            sD = a;
        }
    }
    else if (tN > tD)       // tc > 1 => the t=1 edge is visible
    {
        tN = tD;
        // recompute sc for this edge

        if((-d + b) < 0.0f)
        {
            sN = 0.f;
        }
        else if ((-d + b) > a)
        {
            sN = sD;
        }
        else 
        {
            sN = (-d + b);
            sD = a;
        }
    }

    // finally do the division to get sc and tc
    sc = (osgAbs(sN) < SMALL_NUM ? 0.0f : sN / sD);
    tc = (osgAbs(tN) < SMALL_NUM ? 0.0f : tN / tD);

    // get the difference of the two closest points
    Vec3f dP = w + (sc * u) - (tc * v);  // = S1(sc) - S2(tc)

    return dP.length();   // return the squared distance
}
void ComplexSceneManager::frame(void)
{
#if 0
    setCurrTime(getSystemTime());
    
    if(osgAbs(_sfStartTime.getValue()) < 0.00001)
    {
        setStartTime(_sfCurrTime.getValue());
        
        setLastTime(0.f);
    }
    
    _sfCurrTime.getValue() -= _sfStartTime.getValue();

    if(_sfPaused.getValue() == false)
    {
        SFTime *pSFTimeStamp = editSFTimeStamp();

        if(_sfConstantTime.getValue() == true)
        {
            pSFTimeStamp->getValue() += _sfConstantTimeStep.getValue();

            if(pSFTimeStamp->getValue() < 0.)
                pSFTimeStamp->setValue(0.0);
        }
        else
        {
            pSFTimeStamp->getValue() += 
                (_sfCurrTime.getValue() - _sfLastTime.getValue()) * 
                _sfTimeScale.getValue();
            
            if(pSFTimeStamp->getValue() < 0.)
                pSFTimeStamp->setValue(0.0);
        }
    }
    
    setLastTime(_sfCurrTime.getValue());

    SystemTime = _sfTimeStamp.getValue();

    ++(editSFFrameCount()->getValue());

    if(_sfSensorTask.getValue() != NULL)
    {
        _sfSensorTask.getValue()->frame(_sfTimeStamp.getValue (), 
                                        _sfFrameCount.getValue());
    }
#endif

    if(_sfDumpFrameStart.getValue() == true)
    {
        fprintf(stderr, "=================================================\n");
        fprintf(stderr, "Render Frame\n");
        fprintf(stderr, "=================================================\n");
    }

    FrameHandler::the()->frame();

    SystemTime = FrameHandler::the()->getTimeStamp();

    commitChanges();

    if(_sfDrawManager.getValue() != NULL)
    {
        _sfDrawManager.getValue()->frame(FrameHandler::the()->getTimeStamp(), 
                                         FrameHandler::the()->getFrameCount());
    }
}
void Trackball::updateRotation(Real32 rLastX,    Real32 rLastY, 
                               Real32 rCurrentX, Real32 rCurrentY)
{
    Quaternion qCurrVal;

    Vec3f  gAxis; /* Axis of rotation */
    Real32 rPhi = 0.f;  /* how much to rotate about axis */
    Vec3f  gP1;
    Vec3f  gP2;
    Vec3f  gDiff;
    Real32 rTmp;

    if( (osgAbs(rLastX - rCurrentX) > TypeTraits<Real32>::getDefaultEps()) ||
        (osgAbs(rLastY - rCurrentY) > TypeTraits<Real32>::getDefaultEps())   )
    {
        /*
         * First, figure out z-coordinates for projection of P1 and P2 to
         * deformed sphere
         */
        
        gP1.setValues(  rLastX,
                        rLastY,
                         projectToSphere(_rTrackballSize, rLastX, rLastY));

        gP2.setValues(  rCurrentX,
                        rCurrentY,
                        projectToSphere(_rTrackballSize, rCurrentX, rCurrentY));
        
        /*
         *  Now, we want the cross product of P1 and P2
         */

        gAxis = gP2;
        gAxis.crossThis(gP1);

        /*
         *  Figure out how much to rotate around that axis.
         */

        gDiff = gP2;
        gDiff -= gP1;

        rTmp = gDiff.length() / (2.0f * _rTrackballSize);


        /*
         * Avoid problems with out-of-control values...
         */
        
        if(rTmp > 1.0) 
            rTmp = 1.0;

        if(rTmp < -1.0) 
            rTmp = -1.0;

        if(_gMode == OSGObject)
            rPhi = Real32(-2.0) * osgASin(rTmp);
        else
            rPhi = Real32( 2.0) * osgASin(rTmp);

    }

    rPhi *= _rRotScale;

    if(_bSum == false)
    {
        _qVal.setValueAsAxisRad(gAxis, rPhi);
    }
    else
    {
        qCurrVal.setValueAsAxisRad(gAxis, rPhi);
        _qVal *= qCurrVal;
//        _qVal.multLeft(qCurrVal);
    }
}
void MorphGeometry::updateMorph(void)
{
    if(!getBaseGeometry())
    {
        SWARNING << "No Base Geometry" << std::endl;
        return;
    }

    for(UInt32 i(0) ; i<getMFMorphProperties()->size() ; ++i)
    {
        GeoVectorProperty* BaseProp(getBaseGeometry()->getProperty(getMorphProperties(i)));

        GeoVectorPropertyUnrecPtr Prop(getProperty(getMorphProperties(i)));
        switch(getBlendingMethod())
        {
            case Relative:
                {
                    //Reset the Base mesh
                    UInt32 NumBytesToCopy(Prop->getFormatSize() * BaseProp->size() * BaseProp->getDimension());
                    memcpy(Prop->editData(), BaseProp->getData(), NumBytesToCopy);
                }
                break;
            default:
                SWARNING << "Invalid blending method: " << getBlendingMethod()
                         << ". Using Normalized method." << std::endl;
            case Normalized:
                {
                    Real32 Weight(1.0f);
                    for(UInt32 j(0) ; j < getNumMorphTargets() ; ++j)
                    {
                        Weight -= osgAbs(getMorphTargetWeight(j));
                    }
                    //Zero out the property
                    zeroGeoProperty(Prop);

                    //Call the morph property with the given property format
                    morphGeoProperty(BaseProp, Prop, Weight);
                }
                break;
        }
        setProperty(Prop, getMorphProperties(i));

        //Loop through all morph targets
        Geometry* Target;
        GeoVectorProperty* TargetProp;
        Real32 Weight;
        for(UInt32 j(0) ; j < getNumMorphTargets() ; ++j)
        {
            //If the Weight is really small then don't apply it
            Weight = osgAbs(getMorphTargetWeight(j));
            if(Weight < 0.000001f)
            {
                continue;
            }

            Target = getMorphTarget(j);
            TargetProp = Target->getProperty(getMorphProperties(i));

            //Call the morph property with the given property format
            morphGeoProperty(TargetProp, Prop, Weight);
        }
    }
}
void TextureBackground::clear(DrawEnv  *pEnv)
{
#if !defined(OSG_OGL_COREONLY) || defined(OSG_CHECK_COREONLY)
    TextureBaseChunk *tex = getTexture();

    if(tex == NULL)
    {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        return;
    }

    glPushAttrib(GL_POLYGON_BIT | GL_DEPTH_BUFFER_BIT | 
                 GL_LIGHTING_BIT);

    glDisable(GL_LIGHTING);

#if 1
    // original mode
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#else
    // for testing the grid
    glColor3f(1.0f, 1.0f, 1.0f);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
    glClear(GL_DEPTH_BUFFER_BIT);

    glDisable(GL_DEPTH_TEST);
    glDepthFunc(GL_ALWAYS);
    glDepthMask(GL_FALSE);

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();

    glLoadIdentity();
    glOrtho(0, 1, 0, 1, 0, 1);

    glColor4fv(getColor().getValuesRGBA());

    tex->activate(pEnv);

    if(tex->isTransparent())
    {
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable(GL_BLEND);
    }
    if(osgAbs(getRadialDistortion()) < TypeTraits<Real32>::getDefaultEps())
    {
	    if(getMFTexCoords()->size() < 4)
	    {
	        // set some default texture coordinates.
	        glBegin(GL_QUADS);
                glTexCoord2f(0.0f, 0.0f);
                glVertex3f(0.0f, 0.0f, 0.0f);
                glTexCoord2f(1.0f, 0.0f);
                glVertex3f(1.0f, 0.0f, 0.0f);
                glTexCoord2f(1.0f, 1.0f);
                glVertex3f(1.0f, 1.0f, 0.0f);
                glTexCoord2f(0.0f, 1.0f);
                glVertex3f(0.0f, 1.0f, 0.0f);
	        glEnd();
	    }
	    else
	    {
	        glBegin(GL_QUADS);
            {
                glTexCoord2f(getTexCoords(0).getValues()[0],
                             getTexCoords(0).getValues()[1]);
                glVertex3f(0.0f, 0.0f, 0.0f);
                glTexCoord2f(getTexCoords(1).getValues()[0],
                             getTexCoords(1).getValues()[1]);
                glVertex3f(1.0f, 0.0f, 0.0f);
                glTexCoord2f(getTexCoords(2).getValues()[0],
                             getTexCoords(2).getValues()[1]);
                glVertex3f(1.0f, 1.0f, 0.0f);
                glTexCoord2f(getTexCoords(3).getValues()[0],
                             getTexCoords(3).getValues()[1]);
                glVertex3f(0.0f, 1.0f, 0.0f);
            }
	        glEnd();
	    }
    }
    else // map texture to distortion grid
    {
        updateGrid();
	    Int16 xxmax=getHor()+2,yymax=getVert()+2;

	    UInt32 gridCoords=xxmax*yymax;
	    UInt32 indexArraySize=xxmax*((getVert()+1)*2);

	    if(_vertexCoordArray.size()==gridCoords &&
	       _textureCoordArray.size()==gridCoords &&
	       _indexArray.size()==indexArraySize)
	    {
	        // clear background, because possibly the distortion grid 
            // could not cover th whole window
	        glClearColor(.5f, 0.5f, 0.5f, 1.0f);
	        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	        std::vector<UInt32>::iterator i;
	        UInt32 yMax=getVert()+1;
	        for(UInt32 y=0;y<yMax;y++)
	        {
		        glBegin(GL_TRIANGLE_STRIP);
		        std::vector<UInt32>::iterator begin=_indexArray.begin()+(y*2*xxmax);
		        std::vector<UInt32>::iterator end=begin+2*xxmax;
		        for(std::vector<UInt32>::iterator i=begin;i!=end;i++)
		        {
		            glTexCoord2fv(_textureCoordArray[*i].getValues());
		            glVertex2fv(_vertexCoordArray[*i].getValues());

		        }
		        glEnd();
	        }
	    }
    }
    if(tex->isTransparent())
    {
        glDisable(GL_BLEND);
    }

    tex->deactivate(pEnv);

    Int32 bit = getClearStencilBit();
    
    if (bit >= 0)
    {
        glClearStencil(bit);
        glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    }
    else
    {
        glClear(GL_DEPTH_BUFFER_BIT);
    }

    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glPopAttrib();

    glColor3f(1.0f, 1.0f, 1.0f);
#endif
}
void PhysicsSpace::collisionCallback (dGeomID o1, dGeomID o2)
{
    StatRealElem *NCollisionTestsStatElem = StatCollector::getGlobalElem(PhysicsHandler::statNCollisionTests);
    if(NCollisionTestsStatElem) { NCollisionTestsStatElem->add(1.0f); }

    if (dGeomIsSpace (o1) || dGeomIsSpace (o2))
    {
        // colliding a space with something
        dSpaceCollide2 (o1,o2,reinterpret_cast<void *>(this),&PhysicsSpace::collisionCallback);
        // collide all geoms internal to the space(s)
        if (dGeomIsSpace (o1)) dSpaceCollide (dGeomGetSpace(o1),reinterpret_cast<void *>(this),&PhysicsSpace::collisionCallback);
        if (dGeomIsSpace (o2)) dSpaceCollide (dGeomGetSpace(o2),reinterpret_cast<void *>(this),&PhysicsSpace::collisionCallback);
    }
    else
    {
        _DiscardCollision = false;

        // colliding two non-space geoms, so generate contact
        // points between o1 and o2
        Int32 numContacts = dCollide(o1, o2, _ContactJoints.size(), 
            &(_ContactJoints[0].geom), sizeof(dContact));
    
        StatRealElem *NCollisionsStatElem = StatCollector::getGlobalElem(PhysicsHandler::statNCollisions);
        if(NCollisionsStatElem) { NCollisionsStatElem->add(static_cast<Real32>(numContacts)); }

        if(numContacts>0)
        {
            Vec3f v1,v2,normal;
            Pnt3f position;
            dVector3 odeVec;
            Real32 projectedNormalSpeed;
            for (Int32 i=0; i < numContacts; i++)
            {
                normal += Vec3f(&_ContactJoints[i].geom.normal[0]);
                position += Vec3f(&_ContactJoints[i].geom.pos[0]);
                if(dGeomGetBody(o1))
                {
                    dBodyGetPointVel(dGeomGetBody(o1), _ContactJoints[i].geom.pos[0], _ContactJoints[i].geom.pos[1], _ContactJoints[i].geom.pos[2],odeVec);
                    v1 += Vec3f(&odeVec[0]);
                }
                if(dGeomGetBody(o2))
                {
                    dBodyGetPointVel(dGeomGetBody(o2), _ContactJoints[i].geom.pos[0], _ContactJoints[i].geom.pos[1], _ContactJoints[i].geom.pos[2],odeVec);
                    v2 += Vec3f(&odeVec[0]);
                }
            }

            normal = normal * (1.0f/static_cast<Real32>(numContacts));
            position = position * (1.0f/static_cast<Real32>(numContacts));
            v1 = v1 * (1.0f/static_cast<Real32>(numContacts));
            v2 = v2 * (1.0f/static_cast<Real32>(numContacts));
            projectedNormalSpeed = (v1+v2).projectTo(normal);

            //TODO: Add a way to get the PhysicsGeomUnrecPtr from the GeomIDs so that the PhysicsGeomUnrecPtr can be 
            //sent to the collision event
            produceCollision(position,
                                normal,
                                NULL,
                                NULL,
                                dGeomGetCategoryBits(o1),
                                dGeomGetCollideBits (o1),
                                dGeomGetCategoryBits(o2),
                                dGeomGetCollideBits (o2),
                                v1,
                                v2,
                                osgAbs(projectedNormalSpeed));

            UInt32 Index(0);
            for(; Index<_CollisionListenParamsVec.size() ; ++Index)
            {
                if((dGeomGetCategoryBits(o1) & _CollisionListenParamsVec[Index]._Category) || (dGeomGetCategoryBits(o2) & _CollisionListenParamsVec[Index]._Category))
                {
                    break;
                }
            }
            if(Index < _CollisionListenParamsVec.size())
            {
                for(UInt32 i(0); i<_CollisionListenParamsVec.size() ; ++i)
                {
                    if( ((dGeomGetCategoryBits(o1) & _CollisionListenParamsVec[i]._Category) || (dGeomGetCategoryBits(o2) & _CollisionListenParamsVec[i]._Category)) &&
                         (osgAbs(projectedNormalSpeed) >= _CollisionListenParamsVec[i]._SpeedThreshold)
                        )
                    {
                        //TODO: Add a way to get the PhysicsGeomUnrecPtr from the GeomIDs so that the PhysicsGeomUnrecPtr can be 
                        //sent to the collision event
                        produceCollision(_CollisionListenParamsVec[i]._Listener,
                                position,
                                normal,
                                NULL,
                                NULL,
                                dGeomGetCategoryBits(o1),
                                dGeomGetCollideBits (o1),
                                dGeomGetCategoryBits(o2),
                                dGeomGetCollideBits (o2),
                                v1,
                                v2,
                                osgAbs(projectedNormalSpeed));
                    }
                }
            }
        }
        if(!_DiscardCollision)
        {
            // add these contact points to the simulation
            for (Int32 i=0; i < numContacts; i++)
            {

                getCollisionContact(dGeomGetCategoryBits(o1), dGeomGetCategoryBits(o2))->updateODEContactJoint(_ContactJoints[i]);
                dJointID jointId = dJointCreateContact(_CollideWorldID, 
                    _ColJointGroupId, 
                    &_ContactJoints[i]);

                dJointAttach(jointId, dGeomGetBody(o1), dGeomGetBody(o2));
            }
        }
    }
}
bool Line::intersect(const CylinderVolume &cyl, 
                           Real           &enter,  
                           Real           &exit ) const
{
    Real  radius = cyl.getRadius();

    Vec3r adir;
    Vec3r o_adir;
    Pnt3r apos;

    cyl.getAxis(apos, adir);

    o_adir = adir;
    adir.normalize();

    bool isect;

    Real  ln;
    Real  dl;
    Vec3r RC;
    Vec3r n;
    Vec3r D;

    RC = _pos - apos;

    n  = _dir.cross (adir);
    ln =  n  .length(    );

    if(ln == 0.f)    // IntersectionLine is parallel to CylinderAxis
    {
        D  = RC - (RC.dot(adir)) * adir;
        dl = D.length();

        if(dl <= radius)   // line lies in cylinder
        {
            enter = 0.f;
            exit  = Inf;
        }
        else
        {
            return false;
        }
    }
    else
    {
        n.normalize();

        dl    = osgAbs(RC.dot(n));        //shortest distance
        isect = (dl <= radius);

        if(isect)
        {                 // if ray hits cylinder
            Real  t;
            Real  s;
            Vec3r O;

            O = RC.cross(adir);
            t = - (O.dot(n)) / ln;
            O = n.cross(adir);

            O.normalize();

            s = osgAbs (
                (osgSqrt ((radius * radius) - (dl * dl))) / (_dir.dot(O)));

            exit = t + s;

            if(exit < 0.f)
                return false;

            enter = t - s;

            if(enter < 0.f)
                enter = 0.f;
        }
        else
        {
            return false;
        }
    }

    Real t;

    Plane bottom(-adir, apos);

    if(bottom.intersect(*this, t))
    {
        if(bottom.isInHalfSpace(_pos))
        {
            if(t > enter) 
                enter = t;
        }
        else
        {
            if(t < exit) 
                exit = t;
        }
    }
    else
    {
        if(bottom.isInHalfSpace(_pos))
            return false;
    }
    
    Plane top(adir, apos + o_adir);

    if(top.intersect(*this, t))
    {
        if(top.isInHalfSpace(_pos))
        {
            if(t > enter)
                enter = t;
        }
        else
        {
            if(t < exit)
                exit = t;
        }
    }
    else
    {
        if(top.isInHalfSpace(_pos))
            return false;
    }

    return (enter < exit);
}