From 4ec40ed86509d5b8aaf18ac038836873bb45463c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sun, 29 Dec 2013 01:12:14 -0800 Subject: Updating JSON profile serialization --- LibOVR/Src/OVR_Profile.h | 385 ++++++++++++++++++++++++++++++----------------- 1 file changed, 248 insertions(+), 137 deletions(-) (limited to 'LibOVR/Src/OVR_Profile.h') diff --git a/LibOVR/Src/OVR_Profile.h b/LibOVR/Src/OVR_Profile.h index df25fea..2c5e4c2 100644 --- a/LibOVR/Src/OVR_Profile.h +++ b/LibOVR/Src/OVR_Profile.h @@ -6,7 +6,7 @@ Content : Structs and functions for loading and storing device profile set Created : February 14, 2013 Notes : Profiles are used to store per-user settings that can be transferred and used - across multiple applications. For example, player IPD can be configured once + across multiple applications. For example, player IPD can be configured once and reused for a unified experience across games. Configuration and saving of profiles can be accomplished in game via the Profile API or by the official Oculus Configuration Utility. @@ -25,11 +25,12 @@ otherwise accompanies this software in either electronic or hard copy form. #include "Kernel/OVR_String.h" #include "Kernel/OVR_RefCount.h" #include "Kernel/OVR_Array.h" +#include "Kernel/OVR_Math.h" namespace OVR { // Defines the profile object for each device type -enum ProfileType +enum ProfileDeviceType { Profile_Unknown = 0, Profile_GenericHMD = 10, @@ -37,119 +38,17 @@ enum ProfileType Profile_RiftDKHD = 12, }; -class Profile; - -// ----------------------------------------------------------------------------- -// ***** ProfileManager - -// Profiles are interfaced through a ProfileManager object. Applications should -// create a ProfileManager each time they intend to read or write user profile data. -// The scope of the ProfileManager object defines when disk I/O is performed. Disk -// reads are performed on the first profile access and disk writes are performed when -// the ProfileManager goes out of scope. All profile interactions between these times -// are performed in local memory and are fast. A typical profile interaction might -// look like this: // -// { -// Ptr pm = *ProfileManager::Create(); -// Ptr profile = pm->LoadProfile(Profile_RiftDK1, -// pm->GetDefaultProfileName(Profile_RiftDK1)); -// if (profile) -// { // Retrieve the current profile settings -// } -// } // Profile will be destroyed and any disk I/O completed when going out of scope - -class ProfileManager : public RefCountBase -{ -protected: - // Synchronize ProfileManager access since it may be accessed from multiple threads, - // as it's shared through DeviceManager. - Lock ProfileLock; - Array > ProfileCache; - ProfileType CacheDevice; - String DefaultProfile; - bool Changed; - char NameBuff[32]; - -public: - static ProfileManager* Create(); - - // Static interface functions - int GetProfileCount(ProfileType device); - const char* GetProfileName(ProfileType device, unsigned int index); - bool HasProfile(ProfileType device, const char* name); - Profile* LoadProfile(ProfileType device, unsigned int index); - Profile* LoadProfile(ProfileType device, const char* name); - Profile* GetDeviceDefaultProfile(ProfileType device); - const char* GetDefaultProfileName(ProfileType device); - bool SetDefaultProfileName(ProfileType device, const char* name); - bool Save(const Profile* profile); - bool Delete(const Profile* profile); - -protected: - ProfileManager(); - ~ProfileManager(); - void LoadCache(ProfileType device); - void SaveCache(); - void ClearCache(); - Profile* CreateProfileObject(const char* user, - ProfileType device, - const char** device_name); -}; - -//------------------------------------------------------------------- -// ***** Profile - -// The base profile for all users. This object is not created directly. -// Instead derived device objects provide add specific device members to -// the base profile - -class Profile : public RefCountBase -{ -public: - enum { MaxNameLen = 32 }; - - enum GenderType - { - Gender_Unspecified = 0, - Gender_Male = 1, - Gender_Female = 2 - }; - - ProfileType Type; // The type of device profile - char Name[MaxNameLen]; // The name given to this profile - -protected: - GenderType Gender; // The gender of the user - float PlayerHeight; // The height of the user in meters - float IPD; // Distance between eyes in meters - -public: - virtual Profile* Clone() const = 0; - - // These are properties which are intrinsic to the user and affect scene setup - GenderType GetGender() { return Gender; }; - float GetPlayerHeight() { return PlayerHeight; }; - float GetIPD() { return IPD; }; - float GetEyeHeight(); - - void SetGender(GenderType gender) { Gender = gender; }; - void SetPlayerHeight(float height) { PlayerHeight = height; }; - void SetIPD(float ipd) { IPD = ipd; }; - -protected: - Profile(ProfileType type, const char* name); - - virtual bool ParseProperty(const char* prop, const char* sval); - - friend class ProfileManager; -}; +// HMDProfile and it's child classes, RiftProfile, RiftDk1Profile and +// RiftDKHDProfile represent the intersection of 'per-user' and 'per-device' +// settings. +// //----------------------------------------------------------------------------- // ***** HMDProfile // The generic HMD profile is used for properties that are common to all headsets -class HMDProfile : public Profile +class HmdDevice { protected: // FOV extents in pixels measured by a user @@ -159,8 +58,7 @@ protected: int RR; // right eye outer extent public: - virtual Profile* Clone() const; - + virtual ~HmdDevice() {} void SetLL(int val) { LL = val; }; void SetLR(int val) { LR = val; }; void SetRL(int val) { RL = val; }; @@ -170,13 +68,19 @@ public: int GetLR() { return LR; }; int GetRL() { return RL; }; int GetRR() { return RR; }; + virtual ProfileDeviceType GetDeviceType() const { + return Profile_GenericHMD; + } protected: - HMDProfile(ProfileType type, const char* name); - - virtual bool ParseProperty(const char* prop, const char* sval); - - friend class ProfileManager; + HmdDevice() { + LL = 0; + LR = 0; + RL = 0; + RR = 0; + } + friend class ProfileLoader; + friend class Profile; }; // For headsets that use eye cups @@ -187,52 +91,259 @@ enum EyeCupType EyeCup_C = 2 }; -//----------------------------------------------------------------------------- -// ***** RiftDK1Profile - -// This profile is specific to the Rift Dev Kit 1 and contains overrides specific -// to that device and lens cup settings. -class RiftDK1Profile : public HMDProfile -{ +class RiftDevice : public HmdDevice { protected: EyeCupType EyeCups; // Which eye cup does the player use public: - virtual Profile* Clone() const; - EyeCupType GetEyeCup() { return EyeCups; }; void SetEyeCup(EyeCupType cup) { EyeCups = cup; }; protected: - RiftDK1Profile(const char* name); + RiftDevice() { + EyeCups = EyeCup_A; + } + friend class ProfileLoader; + friend class Profile; +}; - virtual bool ParseProperty(const char* prop, const char* sval); +//----------------------------------------------------------------------------- +// ***** RiftDK1Profile - friend class ProfileManager; +// This profile is specific to the Rift Dev Kit 1 and contains overrides specific +// to that device and lens cup settings. +class RiftDK1Device : public RiftDevice +{ +public: + virtual ProfileDeviceType GetDeviceType() const { + return Profile_RiftDK1; + } + +protected: + RiftDK1Device() { + + } + friend class ProfileLoader; + friend class Profile; }; //----------------------------------------------------------------------------- // ***** RiftDKHDProfile -// This profile is specific to the Rift HD Dev Kit and contains overrides specific +// This profile is specific to the Rift HD Dev Kit and contains overrides specific // to that device and lens cup settings. -class RiftDKHDProfile : public HMDProfile +class RiftDKHDDevice : public RiftDevice { +public: + virtual ProfileDeviceType GetDeviceType() const { + return Profile_RiftDKHD; + } + protected: - EyeCupType EyeCups; // Which eye cup does the player use + RiftDKHDDevice() { + } + friend class ProfileLoader; + friend class Profile; +}; +template +class KeyedObject { public: - virtual Profile* Clone() const; + virtual ~KeyedObject() { + } + virtual const KeyType & GetKey() const = 0; +}; +//------------------------------------------------------------------- +// ***** Profile - EyeCupType GetEyeCup() { return EyeCups; }; - void SetEyeCup(EyeCupType cup) { EyeCups = cup; }; +// The base profile for all users. This object is not created directly. +// +class Profile : public RefCountBase, KeyedObject +{ +public: + enum GenderType + { + Gender_Unspecified = 0, + Gender_Male = 1, + Gender_Female = 2 + }; + + String Name; // The name given to this profile protected: - RiftDKHDProfile(const char* name); + GenderType Gender; // The gender of the user + float PlayerHeight; // The height of the user in meters + float IPD; // Distance between eyes in meters + Quatf StrabismusCorrection; // Amount to rotate modelview matrix to correct for corss-eyed vision + // Should be applied as is to the left eye, and inverted to apply to the + // right eye + HmdDevice Generic; + RiftDK1Device RiftDK1; + RiftDKHDDevice RiftDKHD; - virtual bool ParseProperty(const char* prop, const char* sval); +public: + const String & GetKey() const { return Name; }; + // These are properties which are intrinsic to the user and affect scene setup + GenderType GetGender() const { return Gender; }; + float GetPlayerHeight() const { return PlayerHeight; }; + float GetIPD() const { return IPD; }; + float GetEyeHeight() const; + const Quatf & GetStrabismusCorrection() const { return StrabismusCorrection; }; + HmdDevice & GetGenericDevice() { return Generic; } + RiftDK1Device & GetRiftDK1Device() { return RiftDK1; } + RiftDKHDDevice & GetRiftDKHDDevice() { return RiftDKHD; } + const HmdDevice & GetGenericDevice() const { return Generic; } + const RiftDK1Device& GetRiftDK1Device() const { return RiftDK1; } + const RiftDKHDDevice&GetRiftDKHDDevice() const { return RiftDKHD; } + + void SetGender(GenderType gender) { Gender = gender; }; + void SetPlayerHeight(float height) { PlayerHeight = height; }; + void SetIPD(float ipd) { IPD = ipd; }; + void SetStrabismusCorrection(const Quatf & quat) { StrabismusCorrection = quat; }; + Profile * Clone() const { + return new Profile(*this); + } +protected: + Profile(const char* name); friend class ProfileManager; + friend class ProfileLoader; +}; + + +/* + * A really hacky low-performing associative array, because the + * ProfileManager is attempting to mimic the functionality of one + * + * It should have the same performance characteristics as the + * previous ProfileManager embedded implementation. I'm not write a + * full treemap or hashmap implementaiton because, seriously, there are + * plenty out there in the standard libraries, and if OVR doesn't want to + * use them then I'm not going to get roped into building one just to + * support the policy of never using STL. + * + * Using at(KeyType) instead of operator[KeyType] since the latter would + * interfere with ability to access the index based base class operator[] + */ +template +class AssociativePtrArray : public Array > { +public: + enum { + npos = -1 + }; + + int IndexOf(const KeyType & key) const { + for (int i = 0; i< this->GetSize(); ++i) { + const Ptr & data = this->Data.Data[i]; + if (data && data->GetKey() == key) { + return i; + } + } + return -1; + } + + ValueType * at(const KeyType & key) { + int index = IndexOf(key); + if (npos == index) { + return Ptr(); + } + Ptr & ptr = this->Data.Data[index]; + return ptr; + } + + const ValueType * at(const KeyType & key) const { + int index = IndexOf(key); + if (npos == index) { + return Ptr(); + } + const Ptr & ptr = this->Data.Data[index]; + return ptr; + } +}; + +// ----------------------------------------------------------------------------- +// ***** ProfileManager + +// Profiles are interfaced through a ProfileManager object. Applications should +// create a ProfileManager each time they intend to read or write user profile data. +// The scope of the ProfileManager object defines when disk I/O is performed. Disk +// reads are performed on the first profile access and disk writes are performed when +// the ProfileManager goes out of scope. All profile interactions between these times +// are performed in local memory and are fast. A typical profile interaction might +// look like this: +// +// { +// Ptr pm = *ProfileManager::Create(); +// Ptr profile = pm->LoadProfile(pm->GetDefaultProfileName()); +// if (profile) +// { // Retrieve the current profile settings +// } +// } // Profile will be destroyed and any disk I/O completed when going out of scope + +class ProfileManager : public RefCountBase +{ +protected: + // Synchronize ProfileManager access since it may be accessed from multiple threads, + // as it's shared through DeviceManager. + Lock ProfileLock; + typedef AssociativePtrArray Map; + Map ProfileCache; + String DefaultProfile; + bool Changed; + bool Loaded; + // A container for the name data returned by GetDefaultProfileName() + // which can't be a String, because once acquired by the caller, + // it can't be allowed to be deallocated without risk. Ultimately + // this is caused by the inability to be able to safely return the + // String type from methods, which itself boils down to the problem + // with providing an explicit operator const char *() on a String + // class. Doing so makes it too easy for a caller to get a char * + // to a temporary or have the pointer become deallocated memory + // at some later point unexpectedly. + char NameBuff[32]; + +public: + static ProfileManager* Create(); + + // Static interface functions + unsigned GetProfileCount(); + bool HasProfile(const char* name); + Profile* LoadProfile(const char* name); + Profile* GetDefaultProfile(); + const char* GetDefaultProfileName(); + bool SetDefaultProfileName(const char* name); + + // The previous implementation seemed to imply that if you loaded + // some profiles for a given 'device type' and then saved changes, + // and then loaded a different device type, your changes would be lost + // because the cache would be cleared without ever being persisted to + // disk. The redesign fixes this by eliminating the 'per-device-type' + // profile mechanism. + // + // Profiles represent a users settings and they + // may have different settings for each device, so the profile should + // encapsulate all of them. + // + // Ideally, you should be able to query the top level profile for + // properties related to the hardware based on whatever you're using, + // rather than having to query specifically for generic, DK1 or DKHD + // but the class hierarchy makes that problematic. + bool Save(const Profile * profile); + bool Delete(const Profile * profile); + + // Index based fetching is completely removed. I have no idea what + // was intended to be useful for. Perhaps it was intended as a + // potential optimization since querying by name is O(N) instead of + // O(1), but it's unfathomable that the performance difference would + // ever be noticeable, unless the caller is doing something pathological + // like calling using profile manager every frame, and unlikely even + // then +protected: + ProfileManager(); + ~ProfileManager(); + void LoadCache(); + void SaveCache(); + void ClearCache(); }; @@ -240,4 +351,4 @@ String GetBaseOVRPath(bool create_dir); } -#endif // OVR_Profile_h \ No newline at end of file +#endif // OVR_Profile_h -- cgit v1.2.3