public virtual bool CalcTimeOfImpact(ref Matrix fromA, ref Matrix toA, ref Matrix fromB, ref Matrix toB, CastResult result)
        {
	        m_simplexSolver.Reset();

	        /// compute linear and angular velocity for this interval, to interpolate
            Vector3 linVelA = Vector3.Zero, angVelA = Vector3.Zero, linVelB = Vector3.Zero, angVelB = Vector3.Zero;
	        TransformUtil.CalculateVelocity(ref fromA,ref toA,1f,ref linVelA,ref angVelA);
	        TransformUtil.CalculateVelocity(ref fromB,ref toB,1f,ref linVelB,ref angVelB);

	        float boundingRadiusA = m_convexA.GetAngularMotionDisc();
	        float boundingRadiusB = m_convexB.GetAngularMotionDisc();

	        float maxAngularProjectedVelocity = angVelA.Length() * boundingRadiusA + angVelB.Length() * boundingRadiusB;
	        Vector3 relLinVel = (linVelB-linVelA);
    
	        float relLinVelocLength = relLinVel.Length();

            if (MathUtil.FuzzyZero(relLinVelocLength + maxAngularProjectedVelocity))
            {
		        return false;
            }


    	    float radius = 0.001f;

	        float lambda = 0f;
	        Vector3 v = new Vector3(1,0,0);

	        int maxIter = MAX_ITERATIONS;

	        Vector3 n = Vector3.Zero;
    
	        bool hasResult = false;
	        Vector3 c;

	        float lastLambda = lambda;
	        //btScalar epsilon = btScalar(0.001);

        	int numIter = 0;
	        //first solution, using GJK


	        Matrix identityTrans = Matrix.Identity;

	        SphereShape	raySphere = new SphereShape(0f);
	        raySphere.Margin = 0f;


            //	result.drawCoordSystem(sphereTr);

	        PointCollector	pointCollector1 = new PointCollector();

	        {
		        GjkPairDetector gjk = new GjkPairDetector(m_convexA,m_convexB,m_simplexSolver,m_penetrationDepthSolver);		
		        ClosestPointInput input = new ClosestPointInput();
	
		        //we don't use margins during CCD
	        //	gjk.setIgnoreMargin(true);

		        input.m_transformA = fromA;
		        input.m_transformB = fromB;
		        gjk.GetClosestPoints(input,pointCollector1,null,false);

		        hasResult = pointCollector1.m_hasResult;
		        c = pointCollector1.m_pointInWorld;
	        }

	        if (hasResult)
	        {
		        float dist = pointCollector1.m_distance;
		        n = pointCollector1.m_normalOnBInWorld;

		        float projectedLinearVelocity = Vector3.Dot(relLinVel,n);
        		
		        //not close enough
		        while (dist > radius)
		        {
                    if (result.m_debugDrawer != null)
                    {
                        Vector3 colour = new Vector3(1, 1, 1);
                        result.m_debugDrawer.DrawSphere(ref c, 0.2f, ref colour);
                    } 
                    numIter++;
			        if (numIter > maxIter)
			        {
				        return false; //todo: report a failure
			        }
			        float dLambda = 0f;

			        projectedLinearVelocity = Vector3.Dot(relLinVel,n);

			        //calculate safe moving fraction from distance / (linear+rotational velocity)
        			
			        //btScalar clippedDist  = GEN_min(angularConservativeRadius,dist);
			        //btScalar clippedDist  = dist;
        			
			        //don't report time of impact for motion away from the contact normal (or causes minor penetration)
			        if ((projectedLinearVelocity+ maxAngularProjectedVelocity)<=MathUtil.SIMD_EPSILON)
                    {
				        return false;
        			}
			        dLambda = dist / (projectedLinearVelocity+ maxAngularProjectedVelocity);
        			
			        lambda = lambda + dLambda;

			        if (lambda > 1f || lambda < 0f)
                    {
				        return false;
                    }


			        //todo: next check with relative epsilon
			        if (lambda <= lastLambda)
			        {
				        return false;
				        //n.setValue(0,0,0);
			        }
			        
                    lastLambda = lambda;

			        //interpolate to next lambda
                    Matrix interpolatedTransA = Matrix.Identity, interpolatedTransB = Matrix.Identity, relativeTrans = Matrix.Identity;

			        TransformUtil.IntegrateTransform(ref fromA,ref linVelA,ref angVelA,lambda,ref interpolatedTransA);
			        TransformUtil.IntegrateTransform(ref fromB,ref linVelB,ref angVelB,lambda,ref interpolatedTransB);
                    //relativeTrans = interpolatedTransB.inverseTimes(interpolatedTransA);
                    relativeTrans = MathUtil.InverseTimes(ref interpolatedTransB, ref interpolatedTransA);
                    if (result.m_debugDrawer != null)
                    {
                        result.m_debugDrawer.DrawSphere(interpolatedTransA.Translation, 0.2f, new Vector3(1, 0, 0));
                    }
			        result.DebugDraw( lambda );

			        PointCollector	pointCollector = new PointCollector();
			        GjkPairDetector gjk = new GjkPairDetector(m_convexA,m_convexB,m_simplexSolver,m_penetrationDepthSolver);
			        ClosestPointInput input = new ClosestPointInput();
			        input.m_transformA = interpolatedTransA;
			        input.m_transformB = interpolatedTransB;
			        gjk.GetClosestPoints(input,pointCollector,null,false);
			        if (pointCollector.m_hasResult)
			        {
				        if (pointCollector.m_distance < 0f)
				        {
					        //degenerate ?!
					        result.m_fraction = lastLambda;
					        n = pointCollector.m_normalOnBInWorld;
					        result.m_normal=n;//.setValue(1,1,1);// = n;
					        result.m_hitPoint = pointCollector.m_pointInWorld;
					        return true;
				        }
				        c = pointCollector.m_pointInWorld;		
				        n = pointCollector.m_normalOnBInWorld;
				        dist = pointCollector.m_distance;
			        } else
			        {
				        //??
				        return false;
			        }

		        }

                if ((projectedLinearVelocity + maxAngularProjectedVelocity) <= result.m_allowedPenetration)//SIMD_EPSILON)
                {
                    return false;
                }
        	
		        result.m_fraction = lambda;
		        result.m_normal = n;
		        result.m_hitPoint = c;
		        return true;
	        }

	        return false;

        /*
        //todo:
	        //if movement away from normal, discard result
	        btVector3 move = transBLocalTo.getOrigin() - transBLocalFrom.getOrigin();
	        if (result.m_fraction < btScalar(1.))
	        {
		        if (move.dot(result.m_normal) <= btScalar(0.))
		        {
		        }
	        }
        */


        }