nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: Filename : OVR_OSX_HMDDevice.cpp nuclear@1: Content : OSX Interface to HMD - detects HMD display nuclear@1: Created : September 21, 2012 nuclear@1: Authors : Michael Antonov nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: *************************************************************************************/ nuclear@1: nuclear@1: #include "OVR_OSX_HMDDevice.h" nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: nuclear@1: namespace OVR { namespace OSX { nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, nuclear@1: UInt32 vend, UInt32 prod, const String& displayDeviceName, long dispId) nuclear@1: : DeviceCreateDesc(factory, Device_HMD), nuclear@1: DisplayDeviceName(displayDeviceName), nuclear@1: DesktopX(0), DesktopY(0), Contents(0), nuclear@1: HResolution(0), VResolution(0), HScreenSize(0), VScreenSize(0), nuclear@1: DisplayId(dispId) nuclear@1: { nuclear@1: /* //?????????? nuclear@1: char idstring[9]; nuclear@1: idstring[0] = 'A'-1+((vend>>10) & 31); nuclear@1: idstring[1] = 'A'-1+((vend>>5) & 31); nuclear@1: idstring[2] = 'A'-1+((vend>>0) & 31); nuclear@1: snprintf(idstring+3, 5, "%04d", prod); nuclear@1: DeviceId = idstring;*/ nuclear@1: DeviceId = DisplayDeviceName; nuclear@1: } nuclear@1: nuclear@1: HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) nuclear@1: : DeviceCreateDesc(other.pFactory, Device_HMD), nuclear@1: DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), nuclear@1: DesktopX(other.DesktopX), DesktopY(other.DesktopY), Contents(other.Contents), nuclear@1: HResolution(other.HResolution), VResolution(other.VResolution), nuclear@1: HScreenSize(other.HScreenSize), VScreenSize(other.VScreenSize), nuclear@1: DisplayId(other.DisplayId) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, nuclear@1: DeviceCreateDesc** pcandidate) const nuclear@1: { nuclear@1: if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) nuclear@1: return Match_None; nuclear@1: nuclear@1: // There are several reasons we can come in here: nuclear@1: // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc nuclear@1: // - Require exact device DeviceId/DeviceName match nuclear@1: // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc nuclear@1: // - This DeviceId is empty; becomes candidate nuclear@1: // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc nuclear@1: // - This other.DeviceId is empty; becomes candidate nuclear@1: nuclear@1: const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; nuclear@1: nuclear@1: if ((DeviceId == s2.DeviceId) && nuclear@1: (DisplayId == s2.DisplayId)) nuclear@1: { nuclear@1: // Non-null DeviceId may match while size is different if screen size was overwritten nuclear@1: // by SensorDisplayInfo in prior iteration. nuclear@1: if (!DeviceId.IsEmpty() || nuclear@1: ((HScreenSize == s2.HScreenSize) && nuclear@1: (VScreenSize == s2.VScreenSize)) ) nuclear@1: { nuclear@1: *pcandidate = 0; nuclear@1: return Match_Found; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // DisplayInfo takes precedence, although we try to match it first. nuclear@1: if ((HResolution == s2.HResolution) && nuclear@1: (VResolution == s2.VResolution) && nuclear@1: (HScreenSize == s2.HScreenSize) && nuclear@1: (VScreenSize == s2.VScreenSize)) nuclear@1: { nuclear@1: if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: nuclear@1: *pcandidate = 0; nuclear@1: return Match_Found; nuclear@1: } nuclear@1: nuclear@1: // SensorDisplayInfo may override resolution settings, so store as candidiate. nuclear@1: if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: // OTHER HMD Monitor desc may initialize DeviceName/Id nuclear@1: else if (DeviceId.IsEmpty() && DisplayId == 0) nuclear@1: { nuclear@1: *pcandidate = const_cast((const DeviceCreateDesc*)this); nuclear@1: return Match_Candidate; nuclear@1: } nuclear@1: nuclear@1: return Match_None; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, bool* newDeviceFlag) nuclear@1: { nuclear@1: // This candidate was the the "best fit" to apply sensor DisplayInfo to. nuclear@1: OVR_ASSERT(other.Type == Device_HMD); nuclear@1: nuclear@1: const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; nuclear@1: nuclear@1: // Force screen size on resolution from SensorDisplayInfo. nuclear@1: // We do this because USB detection is more reliable as compared to HDMI EDID, nuclear@1: // which may be corrupted by splitter reporting wrong monitor nuclear@1: if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) nuclear@1: { nuclear@1: // disconnected HMD: replace old descriptor by the 'fake' one. nuclear@1: HScreenSize = s2.HScreenSize; nuclear@1: VScreenSize = s2.VScreenSize; nuclear@1: Contents |= Contents_Screen; nuclear@1: nuclear@1: if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) nuclear@1: { nuclear@1: memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); nuclear@1: Contents |= Contents_Distortion; nuclear@1: } nuclear@1: DeviceId = s2.DeviceId; nuclear@1: DisplayId = s2.DisplayId; nuclear@1: DisplayDeviceName = s2.DisplayDeviceName; nuclear@1: if (newDeviceFlag) *newDeviceFlag = true; nuclear@1: } nuclear@1: else if (DeviceId.IsEmpty()) nuclear@1: { nuclear@1: // This branch is executed when 'fake' HMD descriptor is being replaced by nuclear@1: // the real one. nuclear@1: DeviceId = s2.DeviceId; nuclear@1: DisplayId = s2.DisplayId; nuclear@1: DisplayDeviceName = s2.DisplayDeviceName; nuclear@1: if (newDeviceFlag) *newDeviceFlag = true; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (newDeviceFlag) *newDeviceFlag = false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** HMDDeviceFactory nuclear@1: nuclear@1: HMDDeviceFactory HMDDeviceFactory::Instance; nuclear@1: nuclear@1: void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) nuclear@1: { nuclear@1: CGDirectDisplayID Displays[32]; nuclear@1: uint32_t NDisplays = 0; nuclear@1: CGGetOnlineDisplayList(32, Displays, &NDisplays); nuclear@1: nuclear@1: for (int i = 0; i < NDisplays; i++) nuclear@1: { nuclear@1: io_service_t port = CGDisplayIOServicePort(Displays[i]); nuclear@1: CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kIODisplayMatchingInfo); nuclear@1: nuclear@1: uint32_t vendor = CGDisplayVendorNumber(Displays[i]); nuclear@1: uint32_t product = CGDisplayModelNumber(Displays[i]); nuclear@1: unsigned mwidth = (unsigned)CGDisplayPixelsWide(Displays[i]); nuclear@1: unsigned mheight = (unsigned)CGDisplayPixelsHigh(Displays[i]); nuclear@1: CGRect desktop = CGDisplayBounds(Displays[i]); nuclear@1: nuclear@1: if (vendor == 16082 && product == 1) nuclear@1: { nuclear@1: char idstring[9]; nuclear@1: idstring[0] = 'A'-1+((vendor>>10) & 31); nuclear@1: idstring[1] = 'A'-1+((vendor>>5) & 31); nuclear@1: idstring[2] = 'A'-1+((vendor>>0) & 31); nuclear@1: snprintf(idstring+3, 5, "%04d", product); nuclear@1: nuclear@1: HMDDeviceCreateDesc hmdCreateDesc(this, vendor, product, idstring, Displays[i]); nuclear@1: nuclear@1: if (product == 2) nuclear@1: { nuclear@1: hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, nuclear@1: mwidth, mheight, 0.12096f, 0.06804f); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (hmdCreateDesc.Is7Inch()) nuclear@1: { nuclear@1: // Physical dimension of SLA screen. nuclear@1: hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, nuclear@1: mwidth, mheight, 0.14976f, 0.0936f); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, nuclear@1: mwidth, mheight, 0.12096f, 0.0756f); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %x:%x\n", vendor, product)); nuclear@1: nuclear@1: // Notify caller about detected device. This will call EnumerateAddDevice nuclear@1: // if the this is the first time device was detected. nuclear@1: visitor.Visit(hmdCreateDesc); nuclear@1: } nuclear@1: CFRelease(DispInfo); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() nuclear@1: { nuclear@1: return new HMDDevice(this); nuclear@1: } nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::Is7Inch() const nuclear@1: { nuclear@1: return (strstr(DeviceId.ToCStr(), "OVR0001") != 0) || (Contents & Contents_7Inch); nuclear@1: } nuclear@1: nuclear@1: Profile* HMDDeviceCreateDesc::GetProfileAddRef() const nuclear@1: { nuclear@1: // Create device may override profile name, so get it from there is possible. nuclear@1: ProfileManager* profileManager = GetManagerImpl()->GetProfileManager(); nuclear@1: ProfileType profileType = GetProfileType(); nuclear@1: const char * profileName = pDevice ? nuclear@1: ((HMDDevice*)pDevice)->GetProfileName() : nuclear@1: profileManager->GetDefaultProfileName(profileType); nuclear@1: nuclear@1: return profileName ? nuclear@1: profileManager->LoadProfile(profileType, profileName) : nuclear@1: profileManager->GetDeviceDefaultProfile(profileType); nuclear@1: } nuclear@1: nuclear@1: bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const nuclear@1: { nuclear@1: if ((info->InfoClassType != Device_HMD) && nuclear@1: (info->InfoClassType != Device_None)) nuclear@1: return false; nuclear@1: nuclear@1: bool is7Inch = Is7Inch(); nuclear@1: nuclear@1: OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, nuclear@1: is7Inch ? "Oculus Rift DK1" : nuclear@1: ((HResolution >= 1920) ? "Oculus Rift DK HD" : "Oculus Rift DK1-Prototype") ); nuclear@1: OVR_strcpy(info->Manufacturer, DeviceInfo::MaxNameLength, "Oculus VR"); nuclear@1: info->Type = Device_HMD; nuclear@1: info->Version = 0; nuclear@1: nuclear@1: // Display detection. nuclear@1: if (info->InfoClassType == Device_HMD) nuclear@1: { nuclear@1: HMDInfo* hmdInfo = static_cast(info); nuclear@1: nuclear@1: hmdInfo->DesktopX = DesktopX; nuclear@1: hmdInfo->DesktopY = DesktopY; nuclear@1: hmdInfo->HResolution = HResolution; nuclear@1: hmdInfo->VResolution = VResolution; nuclear@1: hmdInfo->HScreenSize = HScreenSize; nuclear@1: hmdInfo->VScreenSize = VScreenSize; nuclear@1: hmdInfo->VScreenCenter = VScreenSize * 0.5f; nuclear@1: hmdInfo->InterpupillaryDistance = 0.064f; // Default IPD; should be configurable. nuclear@1: hmdInfo->LensSeparationDistance = 0.0635f; nuclear@1: nuclear@1: // Obtain IPD from profile. nuclear@1: Ptr profile = *GetProfileAddRef(); nuclear@1: nuclear@1: if (profile) nuclear@1: { nuclear@1: hmdInfo->InterpupillaryDistance = profile->GetIPD(); nuclear@1: // TBD: Switch on EyeCup type. nuclear@1: } nuclear@1: nuclear@1: if (Contents & Contents_Distortion) nuclear@1: { nuclear@1: memcpy(hmdInfo->DistortionK, DistortionK, sizeof(float)*4); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: if (is7Inch) nuclear@1: { nuclear@1: // 7" screen. nuclear@1: hmdInfo->DistortionK[0] = 1.0f; nuclear@1: hmdInfo->DistortionK[1] = 0.22f; nuclear@1: hmdInfo->DistortionK[2] = 0.24f; nuclear@1: hmdInfo->EyeToScreenDistance = 0.041f; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: hmdInfo->DistortionK[0] = 1.0f; nuclear@1: hmdInfo->DistortionK[1] = 0.18f; nuclear@1: hmdInfo->DistortionK[2] = 0.115f; nuclear@1: nuclear@1: if (HResolution == 1920) nuclear@1: hmdInfo->EyeToScreenDistance = 0.040f; nuclear@1: else nuclear@1: hmdInfo->EyeToScreenDistance = 0.0387f; nuclear@1: } nuclear@1: nuclear@1: hmdInfo->ChromaAbCorrection[0] = 0.996f; nuclear@1: hmdInfo->ChromaAbCorrection[1] = -0.004f; nuclear@1: hmdInfo->ChromaAbCorrection[2] = 1.014f; nuclear@1: hmdInfo->ChromaAbCorrection[3] = 0.0f; nuclear@1: } nuclear@1: nuclear@1: OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), nuclear@1: DisplayDeviceName.ToCStr()); nuclear@1: hmdInfo->DisplayId = DisplayId; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** HMDDevice nuclear@1: nuclear@1: HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) nuclear@1: : OVR::DeviceImpl(createDesc, 0) nuclear@1: { nuclear@1: } nuclear@1: HMDDevice::~HMDDevice() nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: bool HMDDevice::Initialize(DeviceBase* parent) nuclear@1: { nuclear@1: pParent = parent; nuclear@1: nuclear@1: // Initialize user profile to default for device. nuclear@1: ProfileManager* profileManager = GetManager()->GetProfileManager(); nuclear@1: ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType()); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: void HMDDevice::Shutdown() nuclear@1: { nuclear@1: ProfileName.Clear(); nuclear@1: pCachedProfile.Clear(); nuclear@1: pParent.Clear(); nuclear@1: } nuclear@1: nuclear@1: Profile* HMDDevice::GetProfile() const nuclear@1: { nuclear@1: if (!pCachedProfile) nuclear@1: pCachedProfile = *getDesc()->GetProfileAddRef(); nuclear@1: return pCachedProfile.GetPtr(); nuclear@1: } nuclear@1: nuclear@1: const char* HMDDevice::GetProfileName() const nuclear@1: { nuclear@1: return ProfileName.ToCStr(); nuclear@1: } nuclear@1: nuclear@1: bool HMDDevice::SetProfileName(const char* name) nuclear@1: { nuclear@1: pCachedProfile.Clear(); nuclear@1: if (!name) nuclear@1: { nuclear@1: ProfileName.Clear(); nuclear@1: return 0; nuclear@1: } nuclear@1: if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name)) nuclear@1: { nuclear@1: ProfileName = name; nuclear@1: return true; nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: OVR::SensorDevice* HMDDevice::GetSensor() nuclear@1: { nuclear@1: // Just return first sensor found since we have no way to match it yet. nuclear@1: OVR::SensorDevice* sensor = GetManager()->EnumerateDevices().CreateDevice(); nuclear@1: if (sensor) nuclear@1: sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); nuclear@1: return sensor; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: }} // namespace OVR::OSX nuclear@1: nuclear@1: