void TransitionToWorldPointStage::Update(float dt)
            {
                if(m_jumpIfFar && ShouldJumpTo(m_endTransitionInterestPointEcef))
                {
                    m_transitionTime = m_transitionDuration;

                    // Calling SetView here instead of just doing an early-out so that anything that depends on the camera's position will work
                    // (such as mapscene startup searches) [MPLY-8855]
                    Eegeo::v3 endHeadingVector = ComputeHeadingVector(m_endTransitionInterestPointEcef, m_endInterestHeading);
                    Eegeo::Space::EcefTangentBasis newInterestBasis(m_endTransitionInterestPointEcef, endHeadingVector);
                    m_gpsGlobeCameraController.SetView(newInterestBasis, m_endInterestDistance);
                    return;
                }
                
                m_transitionTime += dt;
                float transitionParam = Eegeo::Math::SmoothStep(0.f, 1.f, m_transitionTime / m_transitionDuration);
                
                float interpolatedDistance = Eegeo::Math::Lerp(m_startInterestDistance, m_endInterestDistance, transitionParam);
                Eegeo::dv3 interpolatedInterestPosition = Eegeo::dv3::Lerp(m_startTransitionInterestPointEcef, m_endTransitionInterestPointEcef, transitionParam);
                float currentAssumedAltitude = 0;
                
                m_terrainHeightProvider.TryGetHeight(interpolatedInterestPosition, 0, currentAssumedAltitude);
                
                if(interpolatedInterestPosition.Length() < Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude)
                {
                    interpolatedInterestPosition = interpolatedInterestPosition.Norm() * (Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude);
                }
                
                float interpolatedHeading = Eegeo::Math::Lerp<float>(m_startInterestHeading, m_endInterestHeading, transitionParam);
                Eegeo::v3 interpolatedHeadingVector = ComputeHeadingVector(interpolatedInterestPosition, interpolatedHeading);
                
                Eegeo::Space::EcefTangentBasis newInterestBasis(interpolatedInterestPosition, interpolatedHeadingVector);
                m_gpsGlobeCameraController.SetView(newInterestBasis, interpolatedDistance);
            }
    void PinOverModelExample::MyModelRenderable::Render(Eegeo::Rendering::GLState& glState) const
    {
        Eegeo::m44 transform;
        Eegeo::dv3 location = Eegeo::Space::LatLong::FromDegrees(37.7858,-122.401).ToECEF();
        Eegeo::v3 up(location.Norm().ToSingle());
        Eegeo::v3 forward = (location  - Eegeo::v3(0.f, 1.f, 0.f)).Norm().ToSingle();
        Eegeo::v3 right(Eegeo::v3::Cross(up, forward).Norm());
        forward = Eegeo::v3::Cross(up, right);
        Eegeo::v3 cameraRelativePos = (location - m_renderContext.GetCameraOriginEcef()).ToSingle();
        Eegeo::m44 scaleMatrix;
        scaleMatrix.Scale(1.f);
        Eegeo::m44 cameraRelativeTransform;
        cameraRelativeTransform.SetFromBasis(right, up, forward, cameraRelativePos);
        Eegeo::m44::Mul(transform, cameraRelativeTransform, scaleMatrix);
        transform.SetRow(3, Eegeo::v4(cameraRelativePos, 1.f));

        glState.DepthTest.Enable();
        glState.DepthFunc(GL_LEQUAL);

        //loaded model faces are ccw
        glState.FrontFace(GL_CCW);

        m_model.GetRootNode()->SetVisible(true);
        m_model.GetRootNode()->SetLocalMatrix(transform);
        m_model.GetRootNode()->UpdateRecursive();
        m_model.GetRootNode()->UpdateSphereRecursive();
        m_model.GetRootNode()->DrawRecursive(m_renderContext, m_globalFogging, NULL, true, false);

        glState.FrontFace(GL_CW);

        Eegeo::EffectHandler::Reset();
    }
void CameraTransitioner::Update(float dt)
{
	if(!IsTransitioning())
	{
		return;
	}

	m_transitionTime += dt;
	double transitionParam = Eegeo::Math::SmoothStep(0.0, 1.0, m_transitionTime / m_transitionDuration);

	float interpolatedDistance = Eegeo::Math::Lerp(m_startInterestDistance, m_endInterestDistance, transitionParam);
	Eegeo::dv3 interpolatedInterestPosition = Eegeo::dv3::Lerp(m_startTransitionInterestPoint, m_endTransitionInterestPoint, transitionParam);
	if(interpolatedInterestPosition.LengthSq() < Eegeo::Space::EarthConstants::RadiusSquared)
	{
		interpolatedInterestPosition = interpolatedInterestPosition.Norm() * Eegeo::Space::EarthConstants::Radius;
	}

	float interpolatedHeading = ((1-transitionParam) * m_startTransitionHeading) + (transitionParam * m_endTransitionHeading);

	Eegeo::v3 interpolatedHeadingVector = ComputeHeadingVector(interpolatedInterestPosition, interpolatedHeading);

	Eegeo::Space::EcefTangentBasis newInterestBasis(interpolatedInterestPosition, interpolatedHeadingVector);
	m_cameraController.SetView(newInterestBasis, interpolatedDistance);

	if(transitionParam >= 1.f)
	{
		StopCurrentTransition();
	}
}
    void PODAnimationExample::Draw()
    {
        //create basis around a known location off coast of SF
        Eegeo::m44 transform;
        Eegeo::dv3 location = Eegeo::dv3(4256955.9749164,3907407.6184668,-2700108.75541722);
        Eegeo::v3 up(location.Norm().ToSingle());
        Eegeo::v3 forward = (location  - Eegeo::v3(0.f, 1.f, 0.f)).Norm().ToSingle();
        Eegeo::v3 right(Eegeo::v3::Cross(up, forward).Norm());
        forward = Eegeo::v3::Cross(up, right);
        Eegeo::v3 cameraRelativePos = (location - cameraModel.GetWorldPosition()).ToSingle();
        Eegeo::m44 scaleMatrix;
        scaleMatrix.Scale(1.f);
        Eegeo::m44 cameraRelativeTransform;
        cameraRelativeTransform.SetFromBasis(right, up, forward, cameraRelativePos);
        Eegeo::m44::Mul(transform, cameraRelativeTransform, scaleMatrix);
        transform.SetRow(3, Eegeo::v4(cameraRelativePos, 1.f));
        
        //loaded model faces are ccw
        renderContext.GetGLState().FrontFace(GL_CCW);
        
        pModel->GetRootNode()->SetVisible(true);
        pModel->GetRootNode()->SetLocalMatrix(transform);
        pModel->GetRootNode()->UpdateRecursive();
        pModel->GetRootNode()->UpdateSphereRecursive();
        pModel->GetRootNode()->DrawRecursive(renderContext, globalFogging, NULL, true, false);
       
        renderContext.GetGLState().FrontFace(GL_CW);

    }
            void CameraTransitionController::Update(float dt)
            {
                if(!IsTransitioning())
                {
                    return;
                }

                m_transitionTime += dt;
                float transitionParam = Eegeo::Math::SmoothStep(0.f, 1.f, m_transitionTime / m_transitionDuration);

                float interpolatedDistance = Eegeo::Math::Lerp(m_startInterestDistance, m_endInterestDistance, transitionParam);
                Eegeo::dv3 interpolatedInterestPosition = Eegeo::dv3::Lerp(m_startTransitionInterestPointEcef, m_endTransitionInterestPointEcef, transitionParam);
                float currentAssumedAltitude = 0;

                m_terrainHeightProvider.TryGetHeight(interpolatedInterestPosition, 0, currentAssumedAltitude);

                if(interpolatedInterestPosition.Length() < Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude)
                {
                    interpolatedInterestPosition = interpolatedInterestPosition.Norm() * (Eegeo::Space::EarthConstants::Radius + currentAssumedAltitude);
                }

                float interpolatedHeading = Eegeo::Math::Lerp<float>(m_startInterestHeading, m_endInterestHeading, transitionParam);
                Eegeo::v3 interpolatedHeadingVector = ComputeHeadingVector(interpolatedInterestPosition, interpolatedHeading);

                Eegeo::Space::EcefTangentBasis newInterestBasis(interpolatedInterestPosition, interpolatedHeadingVector);
                m_cameraController.SetView(newInterestBasis, interpolatedDistance);

                if(transitionParam >= 1.f)
                {
                    StopCurrentTransition();
                }
            }
            bool GpsMarkerModel::UpdateGpsPosition(float dt)
            {
                Eegeo::Space::LatLong locationServiceLatLong(0,0);
                if(!m_locationService.GetLocation(locationServiceLatLong))
                {
                    return false;
                }

                float terrainHeight = 0.0f;
                Eegeo::dv3 ecefPositionFlat = locationServiceLatLong.ToECEF();

                if(m_locationService.IsIndoors())
                {
                    double altitude;
                    m_locationService.GetAltitude(altitude);
                    terrainHeight = static_cast<float>(altitude);
                }

                Eegeo::dv3 newLocationEcef = ecefPositionFlat + (ecefPositionFlat.Norm() * terrainHeight);

                float halfLife = 0.25f;
                float jumpThreshold = 50.0f;

                if(m_currentLocationEcef.SquareDistanceTo(newLocationEcef) < jumpThreshold * jumpThreshold)
                {
                    m_currentLocationEcef = m_currentLocationEcef.Norm() * newLocationEcef.Length();
                    m_currentLocationEcef = Eegeo::Helpers::MathsHelpers::ExpMoveTowards(m_currentLocationEcef, newLocationEcef, halfLife, dt, 0.01f);
                }
                else
                {
                    m_currentLocationEcef = newLocationEcef;
                }

                const Eegeo::Space::LatLong& coord = Eegeo::Space::LatLong::FromECEF(m_currentLocationEcef);
                m_blueSphereModel.SetCoordinate(coord);

                const Eegeo::Resources::Interiors::InteriorId& locationServiceInteriorId = m_locationService.GetInteriorId();

                int indoorMapFloorId = 0;
                if (TryGetLocationServiceIndoorMapFloorId(locationServiceInteriorId, indoorMapFloorId))
                {
                    m_blueSphereModel.SetIndoorMap(locationServiceInteriorId.Value(), indoorMapFloorId);
                }
                else
                {
                    m_blueSphereModel.SetIndoorMap(std::string(), 0);
                }

                if(!m_locationService.GetHorizontalAccuracy(m_currentAccuracyMeters))
                {
                    m_currentAccuracyMeters = 0.0f;
                }

                // return true regardless of whether or not we successfully found an indoorMapFloorId
                return true;
            }
Eegeo::v3 ComputeHeadingVector(Eegeo::dv3 interestPosition, float heading)
{
	Eegeo::v3 m_heading(0,1,0);
	Eegeo::dv3 interestEcefUp = interestPosition.Norm();
	Eegeo::v3 m_interestUp = Eegeo::v3(interestEcefUp.GetX(), interestEcefUp.GetY(), interestEcefUp.GetZ());

	Eegeo::v3 m_interestRight = Eegeo::v3::Cross(m_interestUp, m_heading);
	m_interestRight = m_interestRight.Norm();

	m_heading = Eegeo::v3::Cross(m_interestRight, m_interestUp);
	m_heading = m_heading.Norm();

	Eegeo::Quaternion headingRot;
	headingRot.Set(m_interestUp, heading);
	m_heading = headingRot.RotatePoint(m_heading);

	return m_heading;
}
//EnvironmentNotifierExampleTerrainStreamObserver///
void EnvironmentNotifierExampleTerrainStreamObserver::AddSphere(const Eegeo::Streaming::MortonKey& key)
{
	Eegeo::Space::CubeMap::CubeMapCellInfo cellInfo(key);
	const Eegeo::dv2& resourceQuadtreeCellCenter = cellInfo.GetFaceCentre();
	Eegeo::dv3 worldSpaceCellCenter = Eegeo::Space::CubeMap::FacePointToWorld(cellInfo.GetFaceIndex(),
	                                  resourceQuadtreeCellCenter,
	                                  Eegeo::Space::EarthConstants::CubeSideLengthHalf);


	Eegeo::dv3 up = worldSpaceCellCenter.Norm();
	Eegeo::dv3 spherePosition = Eegeo::dv3::Scale(up, Eegeo::Space::EarthConstants::Radius + SPHERE_HEIGHT_ABOVE_SEA);

	Eegeo::v4 color(1.f, 0.f, 1.f, 0.75f);
    
    SphereData sphereData(spherePosition, color);

	m_spheres.insert(std::make_pair(key,sphereData));
}
            Eegeo::v3 ComputeHeadingVector(Eegeo::dv3 interestPosition, float heading)
            {
                Eegeo::v3 interestForward(0,1,0);
                Eegeo::dv3 interestEcefUp = interestPosition.Norm();
                Eegeo::v3 interestUp = interestEcefUp.ToSingle();

                Eegeo::v3 interestRight = Eegeo::v3::Cross(interestUp, interestForward);
                interestRight = interestRight.Norm();

                interestForward = Eegeo::v3::Cross(interestRight, interestUp);
                interestForward = interestForward.Norm();

                Eegeo::Quaternion rotation;
                rotation.Set(interestUp, heading);
                interestForward = rotation.RotatePoint(interestForward);

                return interestForward;
            }
            void SplashScreen::Show()
            {
                
                Eegeo::Space::EcefTangentBasis basis;
                Eegeo::dv3 p = Eegeo::Space::LatLongAltitude::FromDegrees(56.456160, -2.966101, 241.5f).ToECEF();
                Eegeo::Camera::CameraHelpers::EcefTangentBasisFromPointAndHeading(p, -60.53f, basis);
                
                Eegeo::dv3 top(p.ToSingle().Norm());
                Eegeo::dv3 forward(basis.GetForward().Norm());
                Eegeo::dv3 right(Eegeo::dv3::Cross(top, forward));

                m_pStateButtonBuilding->SetEcefPosition((forward*310.f) + (right*4.f) + p);
                m_pStateButtonPOI->SetEcefPosition((forward*310.f) + (right*-28.f) + p);
                m_pStateButtonLocation->SetEcefPosition((forward*310.f) + (right*37.f) + p);
                m_pPlayButton->SetEcefPosition((forward*270.f) + (right*5.f) + (top*-35.f) + p);
                
                m_pStateButtonLocation->Show();
                m_pStateButtonBuilding->Show();
                m_pStateButtonPOI->Show();
                m_pPlayButton->SetItemShouldRender(true);
                m_pSplashScreenModel->SetShouldDisplay(true);
            }
                bool PoiRingTouchController::PerformRayPick(const Eegeo::dv3 &rayOrigin,
                                                            Eegeo::dv3 &rayDirection,
                                                            Eegeo::dv3 &out_rayIntersectionPoint,
                                                            double &out_intersectionParam,
                                                            float &out_terrainHeight,
                                                            float &out_heightAboveTerrain)
                {
                    bool rayPick = false;

                    if(m_appModeModel.GetAppMode() == AppModes::SdkModel::InteriorMode && m_interiorTransitionModel.InteriorIsVisible())
                    {
                        const Eegeo::Resources::Interiors::InteriorsModel* interiorsModel = m_interiorInteractionModel.GetInteriorModel();
                        
                        Eegeo_ASSERT(interiorsModel, "Couldn't get current interiorsModel");
                        
                        const Eegeo::dv3 originNormal = interiorsModel->GetTangentBasis().GetUp();
                        
                        const int selectedFloorIndex = m_interiorInteractionModel.GetSelectedFloorIndex();

                        float floorHeightAboveSeaLevel = Helpers::InteriorHeightHelpers::GetFloorHeightAboveSeaLevel(*interiorsModel, selectedFloorIndex);
                        
                        const Eegeo::dv3 point = originNormal * (floorHeightAboveSeaLevel + Eegeo::Space::EarthConstants::Radius);
                        
                        out_terrainHeight = interiorsModel->GetTangentSpaceBounds().GetMin().y;
                        out_heightAboveTerrain = floorHeightAboveSeaLevel - out_terrainHeight;
                        rayPick = Eegeo::Geometry::IntersectionTests::RayIntersectsWithPlane(rayOrigin, rayDirection, originNormal, point, out_intersectionParam, out_rayIntersectionPoint);
                    }
                    else
                    {
                        rayPick = m_rayPicker.TryGetRayIntersection(rayOrigin, rayDirection, out_rayIntersectionPoint, out_intersectionParam);
                        if(rayPick)
                        {
                            out_terrainHeight = static_cast<float>(out_rayIntersectionPoint.Length() - Eegeo::Space::EarthConstants::Radius);
                            out_heightAboveTerrain = 0.0f;
                        }
                    }
                    if(!rayPick)
                    {
                        rayPick = Eegeo::Geometry::IntersectionTests::GetRayEarthSphereIntersection(rayOrigin, rayDirection, out_rayIntersectionPoint, Eegeo::Space::EarthConstants::RadiusSquared);
                        if(rayPick)
                        {
                            out_terrainHeight = 0.0f;
                            out_heightAboveTerrain = 0.0f;
                            out_intersectionParam = (out_rayIntersectionPoint - rayOrigin).Length();
                        }
                    }

                    return rayPick;
                }
            void WorldPinsInFocusController::Update(float deltaSeconds, const Eegeo::dv3& ecefInterestPoint, const Eegeo::Camera::RenderCamera& renderCamera)
            {
                const IWorldPinsInFocusModel* pClosest = NULL;
                double minDistanceSq = std::numeric_limits<double>::max();
                Eegeo::v2 closestScreenPinLocation;
                Eegeo::v2 screenInterestPoint = ProjectEcefToScreen(ecefInterestPoint, renderCamera);

                if(m_focusEnabled)
                {
                    for(size_t i = 0; i < m_worldPinsRepository.GetItemCount(); ++i)
                    {
                        ExampleApp::WorldPins::SdkModel::WorldPinItemModel* worldPinItemModel = m_worldPinsRepository.GetItemAtIndex(i);

                        if (!worldPinItemModel->IsFocusable())
                        {
                            continue;
                        }
                        
                        Eegeo::dv3 ecefPinLocation;
                        Eegeo::v2 screenPinLocation;

                        m_worldPinsService.GetPinEcefAndScreenLocations(*worldPinItemModel,
                                ecefPinLocation,
                                screenPinLocation);

                        Eegeo::v3 cameraLocal = (ecefPinLocation - renderCamera.GetEcefLocation()).ToSingle();
                        Eegeo::v3 cameraSurfaceNormal = cameraLocal.Norm();

                        Eegeo::v3 upNormal = ecefPinLocation.Norm().ToSingle();
                        float dp = Eegeo::v3::Dot(cameraSurfaceNormal, upNormal);

                        if(dp > 0.0f)
                        {
                            continue;
                        }

                        screenPinLocation = ProjectEcefToScreen(ecefPinLocation, renderCamera);

                        double distanceToFocusSq = (screenInterestPoint - screenPinLocation).LengthSq();

                        if(distanceToFocusSq < minDistanceSq && worldPinItemModel->IsVisible())
                        {
                            pClosest = &worldPinItemModel->GetInFocusModel();
                            minDistanceSq = distanceToFocusSq;
                            closestScreenPinLocation = screenPinLocation;
                        }
                    }
                }

                if(m_pLastFocussedModel != pClosest)
                {
                    m_pLastFocussedModel = pClosest;

                    if(m_pLastFocussedModel != NULL)
                    {
                        m_messageBus.Publish(WorldPinGainedFocusMessage(WorldPinsInFocusModel(m_pLastFocussedModel->GetPinId(),
                                             m_pLastFocussedModel->GetTitle(),
                                             m_pLastFocussedModel->GetSubtitle(),
                                             m_pLastFocussedModel->GetVendor(),
                                             m_pLastFocussedModel->GetJsonData(),
                                             m_pLastFocussedModel->GetRatingsImage(),
                                             m_pLastFocussedModel->GetReviewCount()),
                                             closestScreenPinLocation));
                    }
                    else
                    {
                        m_messageBus.Publish(WorldPinLostFocusMessage());
                    }
                }
                else
                {
                    if(m_pLastFocussedModel != NULL)
                    {
                        m_messageBus.Publish(WorldPinInFocusChangedLocationMessage(closestScreenPinLocation));
                    }
                }
            }
 void WorldMenuController::PositionItems()
 {
     
     if(!m_menuItemsShouldRender)
         return;
     
     Eegeo::m33 headTrackedOrientation;
     Eegeo::m33::Mul(headTrackedOrientation, m_uiCameraProvider.GetBaseOrientation(), m_cachedHeadTracker);
     
     float halfCount = m_viewsByModel.size()/2;
     if(m_viewsByModel.size()%2==0)
         halfCount-=0.5f;
     
     Eegeo::dv3 center = m_uiCameraProvider.GetRenderCameraForUI().GetEcefLocation();
     Eegeo::v3 forward(headTrackedOrientation.GetRow(2));
     Eegeo::v3 top(center.ToSingle().Norm());
     Eegeo::v3 right(Eegeo::v3::Cross(top, forward));
     
     Eegeo::v3 vA = center.ToSingle();
     Eegeo::v3 vB = m_uiCameraProvider.GetOrientation().GetRow(2);
     float angle = Eegeo::Math::Rad2Deg(Eegeo::Math::ACos(Eegeo::v3::Dot(vA, vB)/(vA.Length()*vB.Length())));
     
     
     const float MarginAngle = 85;
     const int PositionMultiplier = 600;
     
     bool shouldUpdatePosition = false;
     
     if(!m_isMenuShown && angle<=MarginAngle)
     {
         m_isMenuShown = true;
         shouldUpdatePosition = true;
         m_cachedHeadTracker = m_uiCameraProvider.GetHeadTrackerOrientation();
         m_cachedCenter = center;
     }
     else if(m_isMenuShown && angle>MarginAngle)
     {
         m_isMenuShown = false;
         shouldUpdatePosition = true;
     }
     
     if ((m_cachedCenter - center).LengthSq() > 1) {
         m_cachedCenter = center;
         shouldUpdatePosition = true;
     }
     
     if(shouldUpdatePosition || m_isMenuShown)
     {
         
         std::vector<WorldMenuItemView*> items;
         for(TViewsByModel::iterator it = m_viewsByModel.begin(); it != m_viewsByModel.end(); ++it)
         {
             std::vector<WorldMenuItemView*>::iterator itItems = items.begin();
             for(; itItems != items.end(); ++itItems)
             {
                 if((*itItems)->GetWorldMenuItem().GetId() < it->second->GetWorldMenuItem().GetId())
                 {
                     break;
                 }
             }
             items.insert(itItems, it->second);
         }
         
         float margin = 0.f;
         
         for(std::vector<WorldMenuItemView*>::iterator it = items.begin(); it != items.end(); ++it)
         {
             WorldMenuItemView* pView = *it;
             if(!m_isMenuShown)
             {
                 pView->SetItemShouldRender(false);
                 m_pSelectedArrow->SetItemShouldRender(false);
                 continue;
             }
             margin += pView->GetWorldMenuItem().GetMarginRight();
             
             Eegeo::dv3 position(center + (forward*PositionMultiplier) + (top*45) + ((right*55*halfCount)-(right*margin)));
             pView->SetEcefPosition(position);
             pView->SetItemShouldRender(true);
             m_pSelectedArrow->SetItemShouldRender(true);
             if(m_menuItemId==pView->GetWorldMenuItem().GetId())
             {
                 m_pSelectedArrow->SetEcefPosition(center + (forward*PositionMultiplier) + (top*92) + (right*55*halfCount)-(right*margin));
             }
             halfCount-=1;
             
             margin += pView->GetWorldMenuItem().GetMarginLeft();
             pView->SetItemShouldRender(m_menuItemsShouldRender);
         }
         
         items.clear();
         m_lastCameraPosition = center;
     }
 }