virtual void operator()( osg::Node *node, osg::NodeVisitor* nv )
   {
      Transform trans;
      mPositionalLight->GetTransform( trans, Transformable::ABS_CS );

      osg::Matrix absMatrix;
      Transformable::GetAbsoluteMatrix( mPositionalLight->GetMatrixNode(), absMatrix );

      osg::Light* osgLight = mPositionalLight->GetLightSource()->getLight();

      float x, y, z, h, p, r;
      trans.Get( x, y, z, h, p, r);

      osg::Vec4 position( x, y, z, 1.0f ); //force positional lighting with w of 1.0f
      osgLight->setPosition( position * osg::Matrix::inverse( absMatrix ) );

      //note: the actual direction is handled by the Transformable's MatrixNode.
      //this just tells the Light that it has a direction.
      osgLight->setDirection( osg::Vec3( 0.0f, 1.0f, 0.0f ) );

      traverse( node, nv );
   }
/**
 * Message handler callback.
 *
 * @param data the message data
 */
void CadworkFlyMotionModel::OnMessage(MessageData* data)
{
   Transformable *target = GetTarget();
   if (target != NULL && IsEnabled() &&
      (data->message == dtCore::System::MESSAGE_POST_EVENT_TRAVERSAL/*MESSAGE_PRE_FRAME*/) &&
      // don't move if paused & using simtime for speed (since simtime will be 0 if paused)
      (!HasOption(OPTION_USE_SIMTIME_FOR_SPEED) || !System::GetInstance().GetPause()))
   {
         if(IsMouseGrabbed())
         {
         // Get the time change (sim time or real time)
         double delta = GetTimeDelta(data);

         Transform transform;

         GetTarget()->GetTransform(transform);

         osg::Vec3 xyz, hpr;

         transform.Get(xyz, hpr);

         // rotation

         bool rotationChanged = false;
         if(!HasOption(OPTION_REQUIRE_MOUSE_DOWN))
         {
            hpr = Rotate(hpr, delta, rotationChanged);
            if (rotationChanged)
            {
               transform.SetRotation(hpr);
            }
         }

         // translation

         bool translationChanged = false;
         xyz = Translate(xyz, delta, translationChanged);
         if (translationChanged)
         {
            transform.SetTranslation(xyz);
         }


         // homing

         if(mHomingButton->GetState())
         {
            GoToHomePosition();
         }


         // finalize changes

         if (rotationChanged || translationChanged)
         {
            GetTarget()->SetTransform(transform);
         }
      }
      // release cursor
      //if(mCursorReleaseButton->GetState())
      //{
      //   ReleaseMouse();
      //}
      ////grab cursor
      //if(mCursorGrabButton->GetState())
      //{
      //   GrabMouse();
      //}
      /**
       * Now some serious work. Below begins the 'animation' of the smooth camera
       * movement while zooming (by mouse wheel). First part is rotation when refocusing
       * and the second part is changing distance. Default animation duration is 0.4s.
       */
      if(mAnimData.isAnimating())
      {
         double f = (System::GetInstance().GetSimulationTime() - mAnimData._startTime) / mAnimData._motion->getDuration();
         f = f<0 ? 0 : f > 1 ? 1 : f; //clamp f into a <0,1> range
         float t;
         mAnimData._motion->getValueInNormalizedRange(f,t); //gets normalized <0,1> linear value f and turns it into non-lineary interpolated value t also normalized (the function is defined in MathMotionTemplate constructor in AnimationData definition)
      
         /** handle amooth zooming */
         if(mAnimData._isZooming)
         {
            float newdist;
            newdist = t * (mAnimData._toDist - mAnimData._fromDist) + mAnimData._fromDist;

            Transform trans;
            target->GetTransform(trans);
            if(mAnimData._isRotating) //rotating while zooming
            {
               osg::Vec3 xyz,hpr,oldhpr;
               trans.Get(xyz,oldhpr);
               trans.Set(xyz,mAnimData._toRotation);
               trans.Get(xyz,hpr);


               float deltaDist = mTmpPrevDistance - newdist;
               /*if (newdist < MIN_DISTANCE && (mAnimData._toDist < mAnimData._fromDist))
               {
                  deltaDist = MIN_DISTANCE - GetDistance();
               }*/
               osg::Vec3 translation (0.0f, deltaDist, 0.0f);
               osg::Matrix mat;
               dtUtil::MatrixUtil::HprToMatrix(mat, hpr);
               translation = osg::Matrix::transform3x3(translation, mat);
               xyz += translation;
               if(mDistanceShouldChange)                  
                  mTmpPrevDistance = newdist;//(mNewCenter-xyz).length();
               trans.Set(xyz,oldhpr);
            }
            else{ //zooming only
               osg::Vec3 xyz,hpr;
               trans.Get(xyz,hpr);
               float deltaDist = mTmpPrevDistance - newdist;
               osg::Vec3 translation (0.0f, deltaDist, 0.0f);
               osg::Matrix mat;
               dtUtil::MatrixUtil::HprToMatrix(mat, hpr);
               translation = osg::Matrix::transform3x3(translation, mat);
               xyz += translation;
               if(mDistanceShouldChange)
                  mTmpPrevDistance = newdist;
               trans.Set(xyz,hpr);
            }
            target->SetTransform(trans);
         }
         
         if(mAnimData._isRotating)
         {
            osg::Quat newRotation;
            newRotation.slerp(f, mAnimData._fromRotation, mAnimData._toRotation);
            if(f>=1.)
            {
               newRotation = mAnimData._toRotation;
            }

            Transform trans;
            target->GetTransform(trans);
            osg::Vec3 xyz;
            trans.GetTranslation(xyz);
            trans.Set(xyz,newRotation);
            target->SetTransform(trans);

            /** cursor interpolation */
            if(mAnimData._interpolateCursor){
                  float newx, newy;
                  float x,y;
                  mMouse->GetPosition(x,y); //get current position in case the user is moving the mouse
                  float oldx, oldy;
                  oldx = mAnimData._previousPhase * (mAnimData._toCursor.x() - mAnimData._fromCursor.x());
                  oldy = mAnimData._previousPhase * (mAnimData._toCursor.y() - mAnimData._fromCursor.y());
                  newx = f * (mAnimData._toCursor.x() - mAnimData._fromCursor.x());// + x/*mAnimData._fromCursor.x()*/;
                  newy = f * (mAnimData._toCursor.y() - mAnimData._fromCursor.y());// + y/*mAnimData._fromCursor.y()*/;
                  float truex, truey;
                  truex = newx - oldx + x;
                  truey = newy - oldy + y;

                  mMouse->SetPosition(truex,truey);
            }
         }

         mAnimData._previousPhase = f;

         if(f>=1.)
         {
            /*Transform trans;
            target->GetTransform(trans);
            trans.Set(trans.GetTranslation(), mAnimData._toRotation);
            target->SetTransform(trans);*/
            mAnimData.reset(); //clear all data, set all flags on false => not animating anymore
         }
      }
   }
}
/**
 * Called when an axis' state has changed.
 *
 * @param axis the changed axis
 * @param oldState the old state of the axis
 * @param newState the new state of the axis
 * @param delta a delta value indicating stateless motion
 */
bool OrbitMotionModel::AxisStateChanged(const Axis* axis,
                                        double oldState,
                                        double newState,
                                        double delta)
{
   delta = delta * (double)mMouseSensitivity;

   if (GetTarget() != 0 && IsEnabled())
   {
      Transform transform;

      GetTarget()->GetTransform(transform);

      osg::Vec3 xyz, hpr;

      transform.Get(xyz, hpr);

      if (axis == mAzimuthAxis.get())
      {
         osg::Vec3 focus(0.0f, mDistance, 0.0f);

         osg::Matrix mat;

         transform.Get(mat);

         dtUtil::MatrixUtil::TransformVec3(focus, mat);

         hpr[0] -= float(delta * mAngularRate);

         osg::Vec3 offset(0.0f, -mDistance, 0.0f);

         dtUtil::MatrixUtil::PositionAndHprToMatrix(mat, focus, hpr);

         dtUtil::MatrixUtil::TransformVec3(xyz, offset, mat);
      }
      else if (axis == mElevationAxis.get())
      {
         osg::Vec3 focus( 0.0f, mDistance, 0.0f );

         osg::Matrix mat;

         transform.Get(mat);

         dtUtil::MatrixUtil::TransformVec3(focus, mat);

         hpr[1] += float(delta * mAngularRate);

         if (hpr[1] < mMinElevationLimit)
         {
            hpr[1] = mMinElevationLimit;
         }
         else if (hpr[1] > mMaxElevationLimit)
         {
            hpr[1] = mMaxElevationLimit;
         }

         osg::Vec3 offset ( 0.0f, -mDistance, 0.0f );

         dtUtil::MatrixUtil::PositionAndHprToMatrix(mat, focus, hpr);

         dtUtil::MatrixUtil::TransformVec3(xyz, offset, mat);
      }
      else if (axis == mDistanceAxis.get())
      {
         float distDelta = -float(delta * mDistance * mLinearRate);

         if (mDistance + distDelta < 1.0f)
         {
            distDelta = 1.0f - mDistance;
         }

         osg::Vec3 translation (0.0f, -distDelta, 0.0f);

         osg::Matrix mat;

         dtUtil::MatrixUtil::HprToMatrix(mat, hpr);

         translation = osg::Matrix::transform3x3(translation, mat);

         xyz += translation;

         mDistance += distDelta;
      }
      else if (axis == mLeftRightTranslationAxis.get())
      {
         osg::Vec3 translation
         (
            -float(delta * mDistance * mLinearRate),
            0.0f,
            0.0f
         );

         osg::Matrix mat;

         dtUtil::MatrixUtil::HprToMatrix(mat, hpr);

         translation = osg::Matrix::transform3x3(translation, mat);

         xyz += translation;
      }
      else if (axis == mUpDownTranslationAxis.get())
      {
         osg::Vec3 translation
         (
            0.0f,
            0.0f,
            -float(delta * mDistance * mLinearRate)
         );

         osg::Matrix mat;

         dtUtil::MatrixUtil::HprToMatrix(mat, hpr);

         translation = osg::Matrix::transform3x3(translation, mat);

         xyz += translation;
      }

      transform.Set(xyz, hpr);

      GetTarget()->SetTransform(transform);

      return true;
   }

   return false;
}