/************************************************************************************

Filename    :   CAPI_HMDState.cpp
Content     :   State associated with a single HMD
Created     :   January 24, 2014
Authors     :   Michael Antonov

Copyright   :   Copyright 2014 Oculus VR, LLC All Rights reserved.

Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 
you may not use the Oculus VR Rift SDK except in compliance with the License, 
which is provided at the time of installation or download, or which 
otherwise accompanies this software in either electronic or hard copy form.

You may obtain a copy of the License at

http://www.oculusvr.com/licenses/LICENSE-3.2 

Unless required by applicable law or agreed to in writing, the Oculus VR SDK 
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

************************************************************************************/

#include "CAPI_HMDState.h"
#include "../OVR_Profile.h"
#include "../Service/Service_NetClient.h"
#ifdef OVR_OS_WIN32
#include "../Displays/OVR_Win32_ShimFunctions.h"
#endif


namespace OVR { namespace CAPI {


//-------------------------------------------------------------------------------------
// ***** HMDState

HMDState::HMDState(const OVR::Service::HMDNetworkInfo& netInfo,
				   const OVR::HMDInfo& hmdInfo,
				   Profile* profile,
				   Service::NetClient* client) :
    pProfile(profile),
    pHmdDesc(0),
    pWindow(0),
    pClient(client),
    NetId(netInfo.NetId),
    NetInfo(netInfo),
    OurHMDInfo(hmdInfo),
    pLastError(NULL),
    EnabledHmdCaps(0),
    EnabledServiceHmdCaps(0),
    SharedStateReader(),
    TheSensorStateReader(),
    TheLatencyTestStateReader(),
    LatencyTestActive(false),
  //LatencyTestDrawColor(),
    LatencyTest2Active(false),
  //LatencyTest2DrawColor(),
    TimeManager(true),
    RenderState(),
    pRenderer(),
    pHSWDisplay(),
    LastFrameTimeSeconds(0.),
    LastGetFrameTimeSeconds(0.),
  //LastGetStringValue(),
    RenderingConfigured(false),
    BeginFrameCalled(false),
    BeginFrameThreadId(),
    RenderAPIThreadChecker(),
    BeginFrameTimingCalled(false)
{
    sharedInit(profile);
}


HMDState::HMDState(const OVR::HMDInfo& hmdInfo, Profile* profile) :
    pProfile(profile),
    pHmdDesc(0),
    pWindow(0),
    pClient(0),
    NetId(InvalidVirtualHmdId),
    NetInfo(),
    OurHMDInfo(hmdInfo),
    pLastError(NULL),
    EnabledHmdCaps(0),
    EnabledServiceHmdCaps(0),
    SharedStateReader(),
    TheSensorStateReader(),
    TheLatencyTestStateReader(),
    LatencyTestActive(false),
  //LatencyTestDrawColor(),
    LatencyTest2Active(false),
  //LatencyTest2DrawColor(),
    TimeManager(true),
    RenderState(),
    pRenderer(),
    pHSWDisplay(),
    LastFrameTimeSeconds(0.),
    LastGetFrameTimeSeconds(0.),
  //LastGetStringValue(),
    RenderingConfigured(false),
    BeginFrameCalled(false),
    BeginFrameThreadId(),
    RenderAPIThreadChecker(),
    BeginFrameTimingCalled(false)
{
    sharedInit(profile);
}

HMDState::~HMDState()
{
    if (pClient)
    {
		pClient->Hmd_Release(NetId);
		pClient = 0;
    }

    ConfigureRendering(0,0,0,0);

    if (pHmdDesc)
    {
        OVR_FREE(pHmdDesc);
        pHmdDesc = NULL;
    }
}

void HMDState::sharedInit(Profile* profile)
{
    // TBD: We should probably be looking up the default profile for the given
    // device type + user if profile == 0.    
    pLastError = 0;

    RenderState.OurHMDInfo = OurHMDInfo;

    UpdateRenderProfile(profile);

    OVR_ASSERT(!pHmdDesc);
    pHmdDesc         = (ovrHmdDesc*)OVR_ALLOC(sizeof(ovrHmdDesc));
    *pHmdDesc        = RenderState.GetDesc();
    pHmdDesc->Handle = this;

    RenderState.ClearColor[0] = 0.0f;
    RenderState.ClearColor[1] = 0.0f;
    RenderState.ClearColor[2] = 0.0f;
    RenderState.ClearColor[3] = 0.0f;

    RenderState.EnabledHmdCaps = 0;

    TimeManager.Init(RenderState.RenderInfo);

    /*
    LatencyTestDrawColor[0] = 0;
    LatencyTestDrawColor[1] = 0;
    LatencyTestDrawColor[2] = 0;
    */

    RenderingConfigured = false;
    BeginFrameCalled   = false;
    BeginFrameThreadId = 0;
    BeginFrameTimingCalled = false;

    // Construct the HSWDisplay. We will later reconstruct it with a specific ovrRenderAPI type if the application starts using SDK-based rendering.
    if(!pHSWDisplay)
    {
        pHSWDisplay = *OVR::CAPI::HSWDisplay::Factory(ovrRenderAPI_None, pHmdDesc, RenderState);
        pHSWDisplay->Enable(pProfile->GetBoolValue("HSW", true));
    }
}

static Vector3f GetNeckModelFromProfile(Profile* profile)
{
    OVR_ASSERT(profile);

    float neckeye[2] = { OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL };
    profile->GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2);

    // Make sure these are vaguely sensible values.
    //OVR_ASSERT((neckeye[0] > 0.05f) && (neckeye[0] < 0.5f));
    //OVR_ASSERT((neckeye[1] > 0.05f) && (neckeye[1] < 0.5f));

    // Named for clarity
    float NeckToEyeHorizontal = neckeye[0];
    float NeckToEyeVertical = neckeye[1];

    // Store the neck model
    return Vector3f(0.0, NeckToEyeVertical, -NeckToEyeHorizontal);
}

static float GetCenterPupilDepthFromRenderInfo(HmdRenderInfo* hmdRenderInfo)
{
    OVR_ASSERT(hmdRenderInfo);

    // Find the distance from the center of the screen to the "center eye"
    // This center eye is used by systems like rendering & audio to represent the player,
    // and they will handle the offsets needed from there to each actual eye.

    // HACK HACK HACK
    // We know for DK1 the screen->lens surface distance is roughly 0.049f, and that the faceplate->lens is 0.02357f.
    // We're going to assume(!!!!) that all HMDs have the same screen->faceplate distance.
    // Crystal Cove was measured to be roughly 0.025 screen->faceplate which agrees with this assumption.
    // TODO: do this properly!  Update:  Measured this at 0.02733 with a CC prototype, CES era (PT7), on 2/19/14 -Steve
    float screenCenterToMidplate = 0.02733f;
    float centerEyeRelief = hmdRenderInfo->GetEyeCenter().ReliefInMeters;
    float CenterPupilDepth = screenCenterToMidplate + hmdRenderInfo->LensSurfaceToMidplateInMeters + centerEyeRelief;

    return CenterPupilDepth;
}

void HMDState::UpdateRenderProfile(Profile* profile)
{
    // Apply the given profile to generate a render context
    RenderState.RenderInfo = GenerateHmdRenderInfoFromHmdInfo(RenderState.OurHMDInfo, profile);
    RenderState.Distortion[0] = CalculateDistortionRenderDesc(StereoEye_Left, RenderState.RenderInfo, 0);
    RenderState.Distortion[1] = CalculateDistortionRenderDesc(StereoEye_Right, RenderState.RenderInfo, 0);

    if (pClient)
    {
        // Center pupil depth
        float centerPupilDepth = GetCenterPupilDepthFromRenderInfo(&RenderState.RenderInfo);
        pClient->SetNumberValue(GetNetId(), "CenterPupilDepth", centerPupilDepth);

        // Neck model
        Vector3f neckModel = GetNeckModelFromProfile(profile);
        double neckModelArray[3] = {
            neckModel.x,
            neckModel.y,
            neckModel.z
        };
        pClient->SetNumberValues(GetNetId(), "NeckModelVector3f", neckModelArray, 3);

        double camerastate[7];
        if (profile->GetDoubleValues(OVR_KEY_CAMERA_POSITION, camerastate, 7) == 0)
        {
            //there is no value, so we load the default
            for (int i = 0; i < 7; i++) camerastate[i] = 0;
            camerastate[3] = 1;//no offset. by default, give the quaternion w component value 1
        }
        else

        TheSensorStateReader.setCenteredFromWorld(OVR::Posed::FromArray(camerastate));
    }


}

HMDState* HMDState::CreateHMDState(NetClient* client, const HMDNetworkInfo& netInfo)
{
    // HMDState works through a handle to service HMD....
    HMDInfo hinfo;
    if (!client->Hmd_GetHmdInfo(netInfo.NetId, &hinfo))
    {
        OVR_DEBUG_LOG(("[HMDState] Unable to get HMD info"));
        return NULL;
    }

#ifdef OVR_OS_WIN32
    OVR_DEBUG_LOG(("Setting up display shim"));

    // Initialize the display shim before reporting the display to the user code
    // so that this will happen before the D3D display object is created.
    Win32::DisplayShim::GetInstance().Update(&hinfo.ShimInfo);
#endif

    Ptr<Profile> pDefaultProfile = *ProfileManager::GetInstance()->GetDefaultUserProfile(&hinfo);
    OVR_DEBUG_LOG(("Using profile %s", pDefaultProfile->GetValue(OVR_KEY_USER)));

    HMDState* hmds = new HMDState(netInfo, hinfo, pDefaultProfile, client);

    if (!hmds->SharedStateReader.Open(netInfo.SharedMemoryName.ToCStr()))
    {
        delete hmds;
        return NULL;
    }

    hmds->TheSensorStateReader.SetUpdater(hmds->SharedStateReader.Get());
    hmds->TheLatencyTestStateReader.SetUpdater(hmds->SharedStateReader.Get());

    return hmds;
}

HMDState* HMDState::CreateHMDState(ovrHmdType hmdType)
{
    HmdTypeEnum t = HmdType_None;
    if (hmdType == ovrHmd_DK1)
        t = HmdType_DK1;    
    else if (hmdType == ovrHmd_DK2)
        t = HmdType_DK2;

    // FIXME: This does not actually grab the right user..
    Ptr<Profile> pDefaultProfile = *ProfileManager::GetInstance()->GetDefaultProfile(t);

    return new HMDState(CreateDebugHMDInfo(t), pDefaultProfile);
}
    

//-------------------------------------------------------------------------------------
// *** Sensor 

bool HMDState::ConfigureTracking(unsigned supportedCaps, unsigned requiredCaps)
{
	return pClient ? pClient->Hmd_ConfigureTracking(NetId, supportedCaps, requiredCaps) : true;
}

void HMDState::ResetTracking()
{
	if (pClient) pClient->Hmd_ResetTracking(NetId);
}        

// Re-center the orientation.
void HMDState::RecenterPose()
{
    TheSensorStateReader.RecenterPose();
}

// Returns prediction for time.
ovrTrackingState HMDState::PredictedTrackingState(double absTime)
{    
	Tracking::TrackingState ss;
    TheSensorStateReader.GetSensorStateAtTime(absTime, ss);

    // Zero out the status flags
    if (!pClient || !pClient->IsConnected(false, false))
    {
        ss.StatusFlags = 0;
    }

    return ss;
}

void HMDState::SetEnabledHmdCaps(unsigned hmdCaps)
{
    if (OurHMDInfo.HmdType < HmdType_DK2)
    {
        // disable low persistence
        hmdCaps &= ~ovrHmdCap_LowPersistence;

        // disable dynamic prediction using the internal latency tester
        hmdCaps &= ~ovrHmdCap_DynamicPrediction;
    }

    if (OurHMDInfo.HmdType >= HmdType_DK2)
    {
        if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_DynamicPrediction)
        {
            // DynamicPrediction change
            TimeManager.ResetFrameTiming(TimeManager.GetFrameTiming().FrameIndex,
                                         (hmdCaps & ovrHmdCap_DynamicPrediction) ? true : false,
                                         RenderingConfigured);
        }
    }

    if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoVSync)
    {
        TimeManager.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true);
    }

    if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoMirrorToWindow)
    {
#ifdef OVR_OS_WIN32
        Win32::DisplayShim::GetInstance().UseMirroring = (hmdCaps & ovrHmdCap_NoMirrorToWindow)  ?
                                                         false : true;
        if (pWindow)
        {   // Force window repaint so that stale mirrored image doesn't persist.
            ::InvalidateRect((HWND)pWindow, 0, true);
        }
#endif
    }

    // TBD: Should this include be only the rendering flags? Otherwise, bits that failed
    //      modification in Hmd_SetEnabledCaps may mis-match...
    EnabledHmdCaps             = hmdCaps & ovrHmdCap_Writable_Mask;
    RenderState.EnabledHmdCaps = EnabledHmdCaps;


    // If any of the modifiable service caps changed, call on the service.
    unsigned prevServiceCaps = EnabledServiceHmdCaps & ovrHmdCap_Writable_Mask;
    unsigned newServiceCaps  = hmdCaps & ovrHmdCap_Writable_Mask & ovrHmdCap_Service_Mask;

    if (prevServiceCaps ^ newServiceCaps)
	{
        EnabledServiceHmdCaps = pClient ? pClient->Hmd_SetEnabledCaps(NetId, newServiceCaps)
                                : newServiceCaps;
    }
}


unsigned HMDState::SetEnabledHmdCaps()
{
	unsigned serviceCaps = pClient ? pClient->Hmd_GetEnabledCaps(NetId) :
                                      EnabledServiceHmdCaps;
    
    return serviceCaps & ((~ovrHmdCap_Service_Mask) | EnabledHmdCaps);    
}


//-------------------------------------------------------------------------------------
// ***** Property Access

// FIXME: Remove the EGetBoolValue stuff and do it with a "Server:" prefix, so we do not
// need to keep a white-list of keys.  This is also way cool because it allows us to add
// new settings keys from outside CAPI that can modify internal server data.

bool HMDState::getBoolValue(const char* propertyName, bool defaultVal)
{
    if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetBoolValue, propertyName))
    {
       return NetClient::GetInstance()->GetBoolValue(GetNetId(), propertyName, defaultVal);
    }
    else if (pProfile)
    {
        return pProfile->GetBoolValue(propertyName, defaultVal);
    }
    return defaultVal;
}

bool HMDState::setBoolValue(const char* propertyName, bool value)
{
	if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetBoolValue, propertyName))
	{
		return NetClient::GetInstance()->SetBoolValue(GetNetId(), propertyName, value);
	}

	return false;
}

int HMDState::getIntValue(const char* propertyName, int defaultVal)
{
    if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetIntValue, propertyName))
    {
        return NetClient::GetInstance()->GetIntValue(GetNetId(), propertyName, defaultVal);
    }
    else if (pProfile)
    {
        return pProfile->GetIntValue(propertyName, defaultVal);
    }
    return defaultVal;
}

bool HMDState::setIntValue(const char* propertyName, int value)
{
	if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetIntValue, propertyName))
	{
		return NetClient::GetInstance()->SetIntValue(GetNetId(), propertyName, value);
	}

	return false;
}

float HMDState::getFloatValue(const char* propertyName, float defaultVal)
{
    if (OVR_strcmp(propertyName, "LensSeparation") == 0)
    {
        return OurHMDInfo.LensSeparationInMeters;
    }
    else if (OVR_strcmp(propertyName, "VsyncToNextVsync") == 0) 
    {
        return OurHMDInfo.Shutter.VsyncToNextVsync;
    }
    else if (OVR_strcmp(propertyName, "PixelPersistence") == 0) 
    {
        return OurHMDInfo.Shutter.PixelPersistence;
    }
    else if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetNumberValue, propertyName))
    {
       return (float)NetClient::GetInstance()->GetNumberValue(GetNetId(), propertyName, defaultVal);
    }
    else if (pProfile)
    {
        return pProfile->GetFloatValue(propertyName, defaultVal);
    }

    return defaultVal;
}

bool HMDState::setFloatValue(const char* propertyName, float value)
{
	if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValue, propertyName))
	{
		return NetClient::GetInstance()->SetNumberValue(GetNetId(), propertyName, value);
	}

	return false;
}

static unsigned CopyFloatArrayWithLimit(float dest[], unsigned destSize,
                                        float source[], unsigned sourceSize)
{
    unsigned count = Alg::Min(destSize, sourceSize);
    for (unsigned i = 0; i < count; i++)
        dest[i] = source[i];
    return count;
}

unsigned HMDState::getFloatArray(const char* propertyName, float values[], unsigned arraySize)
{
	if (arraySize)
	{
		if (OVR_strcmp(propertyName, "ScreenSize") == 0)
		{
			float data[2] = { OurHMDInfo.ScreenSizeInMeters.w, OurHMDInfo.ScreenSizeInMeters.h };

            return CopyFloatArrayWithLimit(values, arraySize, data, 2);
		}
        else if (OVR_strcmp(propertyName, "DistortionClearColor") == 0)
        {
            return CopyFloatArrayWithLimit(values, arraySize, RenderState.ClearColor, 4);
        }
        else if (OVR_strcmp(propertyName, "DK2Latency") == 0)
        {
            if (OurHMDInfo.HmdType != HmdType_DK2)
            {
                return 0;
            }

            union {
                struct X {
                    float latencyRender, latencyTimewarp, latencyPostPresent;
                } x;
                float data[3];
            } m;

            static_assert(sizeof(m.x)==sizeof(m.data), "sizeof(struct X) failure");

            TimeManager.GetLatencyTimings(m.x.latencyRender, m.x.latencyTimewarp, m.x.latencyPostPresent);

            return CopyFloatArrayWithLimit(values, arraySize, m.data, 3);
        }
        else if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetNumberValues, propertyName))
        {
            // Convert floats to doubles
            double* da = new double[arraySize];
            for (int i = 0; i < (int)arraySize; ++i)
            {
                da[i] = values[i];
            }

            int count = NetClient::GetInstance()->GetNumberValues(GetNetId(), propertyName, da, (int)arraySize);

            for (int i = 0; i < count; ++i)
            {
                values[i] = (float)da[i];
            }

            delete[] da;

            return count;
        }
		else if (pProfile)
		{        
			// TBD: Not quite right. Should update profile interface, so that
			//      we can return 0 in all conditions if property doesn't exist.
		
            return pProfile->GetFloatValues(propertyName, values, arraySize);
		}
	}

	return 0;
}

bool HMDState::setFloatArray(const char* propertyName, float values[], unsigned arraySize)
{
    if (!arraySize)
    {
        return false;
    }
    
    if (OVR_strcmp(propertyName, "DistortionClearColor") == 0)
    {
        CopyFloatArrayWithLimit(RenderState.ClearColor, 4, values, arraySize);
        return true;
    }

	if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValues, propertyName))
	{
		double* da = new double[arraySize];
		for (int i = 0; i < (int)arraySize; ++i)
		{
			da[i] = values[i];
		}

		bool result = NetClient::GetInstance()->SetNumberValues(GetNetId(), propertyName, da, arraySize);

		delete[] da;

		return result;
	}

	return false;
}

const char* HMDState::getString(const char* propertyName, const char* defaultVal)
{
    if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetStringValue, propertyName))
    {
        return NetClient::GetInstance()->GetStringValue(GetNetId(), propertyName, defaultVal);
    }

	if (pProfile)
	{
		LastGetStringValue[0] = 0;
		if (pProfile->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue)))
		{
			return LastGetStringValue;
		}
	}

	return defaultVal;
}

bool HMDState::setString(const char* propertyName, const char* value)
{
	if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetStringValue, propertyName))
	{
		return NetClient::GetInstance()->SetStringValue(GetNetId(), propertyName, value);
	}

	return false;
}


//-------------------------------------------------------------------------------------
// *** Latency Test

bool HMDState::ProcessLatencyTest(unsigned char rgbColorOut[3])
{    
    return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut);
}

//-------------------------------------------------------------------------------------
// *** Rendering

bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2],
                                  const ovrFovPort eyeFovIn[2],
                                  const ovrRenderAPIConfig* apiConfig,                                  
                                  unsigned distortionCaps)
{
    ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_ConfigureRendering");

    // null -> shut down.
    if (!apiConfig)
    {
        if (pHSWDisplay)
        {
            pHSWDisplay->Shutdown();
            pHSWDisplay.Clear();
        }

        if (pRenderer)
            pRenderer.Clear();        
        RenderingConfigured = false; 
        return true;
    }

    if (pRenderer &&
        (apiConfig->Header.API != pRenderer->GetRenderAPI()))
    {
        // Shutdown old renderer.
        if (pHSWDisplay)
        {
            pHSWDisplay->Shutdown();
            pHSWDisplay.Clear();
        }

        if (pRenderer)
            pRenderer.Clear();
    }

	distortionCaps = distortionCaps & pHmdDesc->DistortionCaps;

    // Step 1: do basic setup configuration
    RenderState.EnabledHmdCaps = EnabledHmdCaps;     // This is a copy... Any cleaner way?
    RenderState.DistortionCaps = distortionCaps;
    RenderState.EyeRenderDesc[0] = RenderState.CalcRenderDesc(ovrEye_Left,  eyeFovIn[0]);
    RenderState.EyeRenderDesc[1] = RenderState.CalcRenderDesc(ovrEye_Right, eyeFovIn[1]);
    eyeRenderDescOut[0] = RenderState.EyeRenderDesc[0];
    eyeRenderDescOut[1] = RenderState.EyeRenderDesc[1];

    TimeManager.ResetFrameTiming(0,
                                 (EnabledHmdCaps & ovrHmdCap_DynamicPrediction) ? true : false,
                                 true);

    LastFrameTimeSeconds = 0.0f;

    // Set RenderingConfigured early to avoid ASSERTs in renderer initialization.
    RenderingConfigured = true;

    if (!pRenderer)
    {
        pRenderer = *DistortionRenderer::APICreateRegistry
                        [apiConfig->Header.API](pHmdDesc, TimeManager, RenderState);
    }

    if (!pRenderer ||
        !pRenderer->Initialize(apiConfig))
    {
        RenderingConfigured = false;
        return false;
    }    

    // Setup the Health and Safety Warning display system.
    if(pHSWDisplay && (pHSWDisplay->GetRenderAPIType() != apiConfig->Header.API)) // If we need to reconstruct the HSWDisplay for a different graphics API type, delete the existing display.
    {
        pHSWDisplay->Shutdown();
        pHSWDisplay.Clear();
    }

    if(!pHSWDisplay) // Use * below because that for of operator= causes it to inherit the refcount the factory gave the object.
    {
        pHSWDisplay = *OVR::CAPI::HSWDisplay::Factory(apiConfig->Header.API, pHmdDesc, RenderState);
        pHSWDisplay->Enable(pProfile->GetBoolValue("HSW", true));
    }

    if (pHSWDisplay)
        pHSWDisplay->Initialize(apiConfig); // This is potentially re-initializing it with a new config.

    return true;
}


void  HMDState::SubmitEyeTextures(const ovrPosef renderPose[2],
                                  const ovrTexture eyeTexture[2])
{
    RenderState.EyeRenderPoses[0] = renderPose[0];
    RenderState.EyeRenderPoses[1] = renderPose[1];

    if (pRenderer)
    {
        pRenderer->SubmitEye(0, &eyeTexture[0]);
        pRenderer->SubmitEye(1, &eyeTexture[1]);
    }
}


// I appreciate this is not an idea place for this function, but it didn't seem to be
// being linked properly when in OVR_CAPI.cpp. 
// Please relocate if you know of a better place
ovrBool ovrHmd_CreateDistortionMeshInternal( ovrHmdStruct *  hmd,
                                             ovrEyeType eyeType, ovrFovPort fov,
                                             unsigned int distortionCaps,
                                             ovrDistortionMesh *meshData,
											 float overrideEyeReliefIfNonZero )
{
    if (!meshData)
        return 0;
    HMDState* hmds = (HMDState*)hmd;

    // Not used now, but Chromatic flag or others could possibly be checked for in the future.
    OVR_UNUSED1(distortionCaps); 
   
#if defined (OVR_CC_MSVC)
    static_assert(sizeof(DistortionMeshVertexData) == sizeof(ovrDistortionVertex), "DistortionMeshVertexData size mismatch");
#endif
	
    // *** Calculate a part of "StereoParams" needed for mesh generation

    // Note that mesh distortion generation is invariant of RenderTarget UVs, allowing
    // render target size and location to be changed after the fact dynamically. 
    // eyeToSourceUV is computed here for convenience, so that users don't need
    // to call ovrHmd_GetRenderScaleAndOffset unless changing RT dynamically.

    const HmdRenderInfo&  hmdri          = hmds->RenderState.RenderInfo;    
    StereoEye             stereoEye      = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right;

    DistortionRenderDesc& distortion = hmds->RenderState.Distortion[eyeType];
	if (overrideEyeReliefIfNonZero)
	{
		distortion.Lens = GenerateLensConfigFromEyeRelief(overrideEyeReliefIfNonZero,hmdri);
	}

    // Find the mapping from TanAngle space to target NDC space.
    ScaleAndOffset2D      eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov);

    int triangleCount = 0;
    int vertexCount = 0;

    DistortionMeshCreate((DistortionMeshVertexData**)&meshData->pVertexData,
                         (uint16_t**)&meshData->pIndexData,
                          &vertexCount, &triangleCount,
                          (stereoEye == StereoEye_Right),
                          hmdri, distortion, eyeToSourceNDC);

    if (meshData->pVertexData)
    {
        // Convert to index
        meshData->IndexCount = triangleCount * 3;
        meshData->VertexCount = vertexCount;
        return 1;
    }

    return 0;
}



}} // namespace OVR::CAPI
