void ExtendedWmlCamera::OrbitVertical( float fAngle )
{
	Matrix3f rotate;
	rotate.FromAxisAngle( GetLeft(), fAngle );
	
	Vector3f direction( GetLocation() - m_ptTarget );
	Vector3f newUp( rotate * GetUp() );
	newUp.Normalize();

	SetTargetFrame( m_ptTarget + (rotate * direction), GetLeft(), 
		newUp, m_ptTarget );
}
void ExtendedWmlCamera::RotateLateral( float fAngle )
{
	Matrix3f rotate;
	rotate.FromAxisAngle( GetUp(), fAngle );

	Vector3f direction( m_ptTarget - GetLocation () );

	Vector3f newLeft( rotate * GetLeft() );
	newLeft.Normalize();

	SetTargetFrame( GetLocation(), newLeft, GetUp(), 
		GetLocation() + ( rotate * direction ) );
}
//----------------------------------------------------------------------------
bool RipplingOcean::Setup ()
{
    m_bStopped = false;
    m_fStopTime = GetTimeInSeconds();

    m_spkScene = new Node(1);
    m_spkTrnNode = new Node(1);
    m_spkScene->AttachChild(m_spkTrnNode);

    // Root node for a scene graph that contains a bump-mapped triangle mesh
    // square.
    m_spkModel = new Node(1);

    // create the triangle mesh surface
    TriMesh* pkMesh = NULL;
    CreateRectangleMesh(pkMesh, Vector3f::ZERO,
        Vector3f::UNIT_X, Vector3f::UNIT_Y, -Vector3f::UNIT_Z,
        1400.0f, 1200.0f, 50, 50, true, true, true);

    m_spkTriMesh = pkMesh;

    m_spkTriMesh->SetVertexShader(m_spkVertShader);
    m_spkTriMesh->SetPixelShader(m_spkPixShader);
    SetupShaders();

    Image* pkNormal = Image::Load("plasma.mif");
    if ( !pkNormal )
        return false;

    HeightToNormalMap( pkNormal );

    Texture* pkNormalTex = new Texture;
    pkNormalTex->SetImage(pkNormal);
    pkNormalTex->Mipmap() = Texture::MM_LINEAR_LINEAR;
    pkNormalTex->Filter() = Texture::FM_LINEAR;
    pkNormalTex->Apply() = Texture::AM_DECAL;
    pkNormalTex->Wrap() = Texture::WM_WRAP_S_WRAP_T;
    TextureState* pkTS = new TextureState;
    pkTS->Set(0,pkNormalTex);

    Image* pkWater = Image::Load("watergradient.mif");
    if (!pkWater)
        return false;

    Texture* pkWaterTex = new Texture;
    pkWaterTex->SetImage(pkWater);
    pkWaterTex->Apply() = Texture::AM_DECAL;
    pkWaterTex->Wrap() = Texture::WM_CLAMP_S_CLAMP_T;
    pkTS->Set(1,pkWaterTex);

    Image* pkSkySphere = Image::Load("sky.mif");
    if (!pkSkySphere)
        return false;

    Texture* pkSkySphereTex = new Texture;
    pkSkySphereTex->SetImage(pkSkySphere);
    pkTS->Set(2,pkSkySphereTex);

    m_spkTriMesh->SetRenderState(pkTS);

    m_spkModel->AttachChild(m_spkTriMesh);
    m_spkTrnNode->AttachChild(m_spkModel);

    // I'll admit that this is kind of a hack, but it puts the sun
    // a smidge higher in the sky.  It makes it look nicest to start.  =)
    Matrix3f kIncr;
    kIncr.FromAxisAngle(Vector3f::UNIT_X, -0.08f);
    m_spkTrnNode->Rotate() = kIncr; 

    return true;
}
//----------------------------------------------------------------------------
void DrawImplicitSurface::OnSpecialKeyDown (int iKey, int, int)
{
    // TO DO:  These are chosen for the specific functions in this
    // application.  Allow the application to modify these, either by key
    // strokes or automatically.
    const float fTrnDelta = 0.25f, fRotDelta = 0.1f;

    float fLength;
    Matrix3f kRot;

    bool bMoved = false;

    if ( iKey == KEY_UP_ARROW )
    {
        // translate forward in camera direction
        m_kRT.Location() += fTrnDelta*m_kRT.Direction();
        bMoved = true;
    }
    else if ( iKey == KEY_DOWN_ARROW )
    {
        // translate backward in camera direction
        m_kRT.Location() -= fTrnDelta*m_kRT.Direction();
        bMoved = true;
    }
    else if ( iKey == KEY_F1 )
    {
        // rotate about camera right, move up on view sphere
        fLength = m_kRT.Location().Length();
        kRot.FromAxisAngle(m_kRT.Right(),fRotDelta);
        m_kRT.Direction() = kRot*m_kRT.Direction();
        m_kRT.Up() = kRot*m_kRT.Up();
        m_kRT.Location() = -fLength*m_kRT.Direction();
        bMoved = true;
    }
    else if ( iKey == KEY_F2 )
    {
        // rotate about camera right, move down on view sphere
        fLength = m_kRT.Location().Length();
        kRot.FromAxisAngle(m_kRT.Right(),-fRotDelta);
        m_kRT.Direction() = kRot*m_kRT.Direction();
        m_kRT.Up() = kRot*m_kRT.Up();
        m_kRT.Location() = -fLength*m_kRT.Direction();
        bMoved = true;
    }
    else if ( iKey == KEY_F3 )
    {
        // rotate about camera up, move right on view sphere
        fLength = m_kRT.Location().Length();
        kRot.FromAxisAngle(m_kRT.Up(),fRotDelta);
        m_kRT.Direction() = kRot*m_kRT.Direction();
        m_kRT.Right() = kRot*m_kRT.Right();
        m_kRT.Location() = -fLength*m_kRT.Direction();
        bMoved = true;
    }
    else if ( iKey == KEY_F4 )
    {
        // rotate about camera up, move left on view sphere
        fLength = m_kRT.Location().Length();
        kRot.FromAxisAngle(m_kRT.Up(),-fRotDelta);
        m_kRT.Direction() = kRot*m_kRT.Direction();
        m_kRT.Right() = kRot*m_kRT.Right();
        m_kRT.Location() = -fLength*m_kRT.Direction();
        bMoved = true;
    }
    else if ( iKey == KEY_F5 )
    {
        // rotate about camera direction, roll counterclockwise
        fLength = m_kRT.Location().Length();
        kRot.FromAxisAngle(m_kRT.Direction(),fRotDelta);
        m_kRT.Up() = kRot*m_kRT.Up();
        m_kRT.Right() = kRot*m_kRT.Right();
        bMoved = true;
    }
    else if ( iKey == KEY_F6 )
    {
        // rotate about camera direction, roll clockwise
        fLength = m_kRT.Location().Length();
        kRot.FromAxisAngle(m_kRT.Direction(),-fRotDelta);
        m_kRT.Up() = kRot*m_kRT.Up();
        m_kRT.Right() = kRot*m_kRT.Right();
        bMoved = true;
    }

    if ( bMoved )
    {
        m_kRT.DrawSurface(m_iMaxSample,m_kRT.Direction(),m_bBlur);
        OnDisplay();
    }
}