summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad Davis <[email protected]>2013-12-29 11:58:10 -0800
committerBrad Davis <[email protected]>2013-12-29 11:58:10 -0800
commit9d8604072f5e26c4179f5a399a28d1ffb1b669a2 (patch)
treefe7e7f63ea491f9ad0acb22cefb616d17a054b19
parent4ec40ed86509d5b8aaf18ac038836873bb45463c (diff)
Revert "Updating JSON profile serialization"
This reverts commit 4ec40ed86509d5b8aaf18ac038836873bb45463c.
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.cpp11
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.h12
-rw-r--r--LibOVR/Src/OVR_Profile.cpp1013
-rw-r--r--LibOVR/Src/OVR_Profile.h385
4 files changed, 716 insertions, 705 deletions
diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.cpp b/LibOVR/Src/OVR_Linux_HMDDevice.cpp
index 52f537d..cce37d5 100644
--- a/LibOVR/Src/OVR_Linux_HMDDevice.cpp
+++ b/LibOVR/Src/OVR_Linux_HMDDevice.cpp
@@ -234,13 +234,14 @@ Profile* HMDDeviceCreateDesc::GetProfileAddRef() const
{
// Create device may override profile name, so get it from there is possible.
ProfileManager* profileManager = GetManagerImpl()->GetProfileManager();
+ ProfileType profileType = GetProfileType();
const char * profileName = pDevice ?
((HMDDevice*)pDevice)->GetProfileName() :
- profileManager->GetDefaultProfileName();
+ profileManager->GetDefaultProfileName(profileType);
return profileName ?
- profileManager->LoadProfile(profileName) :
- profileManager->GetDefaultProfile();
+ profileManager->LoadProfile(profileType, profileName) :
+ profileManager->GetDeviceDefaultProfile(profileType);
}
@@ -340,7 +341,7 @@ bool HMDDevice::Initialize(DeviceBase* parent)
// Initialize user profile to default for device.
ProfileManager* profileManager = GetManager()->GetProfileManager();
- ProfileName = profileManager->GetDefaultProfileName();
+ ProfileName = profileManager->GetDefaultProfileName(getDesc()->GetProfileType());
return true;
}
@@ -371,7 +372,7 @@ bool HMDDevice::SetProfileName(const char* name)
ProfileName.Clear();
return 0;
}
- if (GetManager()->GetProfileManager()->HasProfile(name))
+ if (GetManager()->GetProfileManager()->HasProfile(getDesc()->GetProfileType(), name))
{
ProfileName = name;
return true;
diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.h b/LibOVR/Src/OVR_Linux_HMDDevice.h
index 2350958..d0585d1 100644
--- a/LibOVR/Src/OVR_Linux_HMDDevice.h
+++ b/LibOVR/Src/OVR_Linux_HMDDevice.h
@@ -84,10 +84,10 @@ public:
virtual bool GetDeviceInfo(DeviceInfo* info) const;
// Requests the currently used default profile. This profile affects the
- // settings reported by HMDInfo.
+ // settings reported by HMDInfo.
Profile* GetProfileAddRef() const;
- ProfileDeviceType GetProfileType() const
+ ProfileType GetProfileType() const
{
return (HResolution >= 1920) ? Profile_RiftDKHD : Profile_RiftDK1;
}
@@ -120,26 +120,26 @@ public:
// HMDDevice represents an Oculus HMD device unit. An instance of this class
// is typically created from the DeviceManager.
-// After HMD device is created, we its sensor data can be obtained by
+// After HMD device is created, we its sensor data can be obtained by
// first creating a Sensor object and then wrappig it in SensorFusion.
class HMDDevice : public DeviceImpl<OVR::HMDDevice>
{
public:
HMDDevice(HMDDeviceCreateDesc* createDesc);
- ~HMDDevice();
+ ~HMDDevice();
virtual bool Initialize(DeviceBase* parent);
virtual void Shutdown();
// Requests the currently used default profile. This profile affects the
- // settings reported by HMDInfo.
+ // settings reported by HMDInfo.
virtual Profile* GetProfile() const;
virtual const char* GetProfileName() const;
virtual bool SetProfileName(const char* name);
// Query associated sensor.
- virtual OVR::SensorDevice* GetSensor();
+ virtual OVR::SensorDevice* GetSensor();
protected:
HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); }
diff --git a/LibOVR/Src/OVR_Profile.cpp b/LibOVR/Src/OVR_Profile.cpp
index bf8906e..3be43db 100644
--- a/LibOVR/Src/OVR_Profile.cpp
+++ b/LibOVR/Src/OVR_Profile.cpp
@@ -5,9 +5,9 @@ Filename : OVR_Profile.cpp
Content : Structs and functions for loading and storing device profile settings
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.
@@ -21,13 +21,12 @@ otherwise accompanies this software in either electronic or hard copy form.
************************************************************************************/
#include "OVR_Profile.h"
-#include "OVR_Log.h"
+#include "OVR_JSON.h"
#include "Kernel/OVR_Types.h"
#include "Kernel/OVR_SysFile.h"
#include "Kernel/OVR_Allocator.h"
#include "Kernel/OVR_Array.h"
-#include <json/json.h>
-#include <fstream>
+
#ifdef OVR_OS_WIN32
#include <Shlobj.h>
#else
@@ -41,33 +40,8 @@ otherwise accompanies this software in either electronic or hard copy form.
#endif
-
-#define PROFILE_VERSION 2
-#define MAX_PROFILE_MAJOR_VERSION 2
-
-// Many hard coded strings used in numerous locations have been
-// repositioned here, so that there's no chance of a misspelling
-// causing a problem. Not every string has been moved, but most of the
-// repeated ones have.
-#define KEY_PROFILE_VERSION "Oculus Profile Version"
-#define KEY_CURRENT_PROFILE "CurrentProfile"
-#define KEY_PROFILES "Profiles"
-#define KEY_DEVICES "Devices"
-#define KEY_GENDER "Gender"
-#define KEY_PLAYER_HEIGHT "PlayerHeight"
-#define KEY_IPD "IPD"
-#define KEY_STRABISMUS_CORRECTION "StrabismusCorrection"
-
-#define KEY_LL "LL"
-#define KEY_LR "LR"
-#define KEY_RL "RL"
-#define KEY_RR "RR"
-#define KEY_EYECUP "EyeCup"
-#define EPSILON 0.00001f
-// 5'10" inch man
-#define DEFAULT_HEIGHT 1.778f
-#define DEFAULT_IPD 0.064f
-
+#define PROFILE_VERSION 1.0
+#define MAX_PROFILE_MAJOR_VERSION 1
namespace OVR {
@@ -82,7 +56,7 @@ String GetBaseOVRPath(bool create_dir)
TCHAR data_path[MAX_PATH];
SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path);
path = String(data_path);
-
+
path += "/Oculus";
if (create_dir)
@@ -93,11 +67,11 @@ String GetBaseOVRPath(bool create_dir)
DWORD attrib = GetFileAttributes(wpath);
bool exists = attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY);
if (!exists)
- {
+ {
CreateDirectory(wpath, NULL);
}
}
-
+
#elif defined(OVR_OS_MAC)
const char* home = getenv("HOME");
@@ -118,21 +92,11 @@ String GetBaseOVRPath(bool create_dir)
}
#else
- // Updated the config folder location logic to rely on the
- // XDG specification for config locations (the XDK location was being
- // used anyway, but was hardcoded). This is analgous to using
- // SHGetFolderPath in the windows implementation, rather than
- // hardcoding %HOME%/AppData/Local
- const char * config_home = getenv("XDG_CONFIG_HOME");
- if (NULL != config_home) {
- path = config_home;
- } else {
- // only if XDG_CONFIG_HOME is unses does the specification say to
- // fallback on the default of $HOME/.config
- path = getenv("HOME");
- path += "/.config";
- }
- path += "/Oculus";
+
+ passwd* pwd = getpwuid(getuid());
+ const char* home = pwd->pw_dir;
+ path = home;
+ path += "/.config/Oculus";
if (create_dir)
{ // Create the Oculus directory if it doesn't exist
@@ -165,7 +129,7 @@ String GetProfilePath(bool create_dir)
ProfileManager::ProfileManager()
{
Changed = false;
- Loaded = false;
+ CacheDevice = Profile_Unknown;
}
ProfileManager::~ProfileManager()
@@ -182,423 +146,410 @@ ProfileManager* ProfileManager::Create()
return new ProfileManager();
}
+Profile* ProfileManager::CreateProfileObject(const char* user,
+ ProfileType device,
+ const char** device_name)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ Profile* profile = NULL;
+ switch (device)
+ {
+ case Profile_GenericHMD:
+ *device_name = NULL;
+ profile = new HMDProfile(Profile_GenericHMD, user);
+ break;
+ case Profile_RiftDK1:
+ *device_name = "RiftDK1";
+ profile = new RiftDK1Profile(user);
+ break;
+ case Profile_RiftDKHD:
+ *device_name = "RiftDKHD";
+ profile = new RiftDKHDProfile(user);
+ break;
+ case Profile_Unknown:
+ break;
+ }
+
+ return profile;
+}
+
+
// Clear the local profile cache
void ProfileManager::ClearCache()
{
Lock::Locker lockScope(&ProfileLock);
+
ProfileCache.Clear();
- Loaded = false;
+ CacheDevice = Profile_Unknown;
}
-// Profile loader is an intermediary that allows me to break down the
-// serialization into smaller pieces that work directly against the
-// new native JSON container type. This can't be done in the
-// ProfileManager because it can't declare functions referencing the
-// new type without polluting the header with knowledge of the JSON
-// implementation
-class ProfileLoader {
-public:
- static void loadHmd(HmdDevice & device, const Json::Value & node) {
- if (node.isNull()) {
- return;
- }
- if (node.isMember(KEY_LL)) {
- device.LL = node.get(KEY_LL, 0).asInt();
- }
- if (node.isMember(KEY_LR)) {
- device.LR = node.get(KEY_LR, 0).asInt();
- }
- if (node.isMember(KEY_RL)) {
- device.RL = node.get(KEY_RL, 0).asInt();
- }
- if (node.isMember(KEY_RR)) {
- device.RR = node.get(KEY_RR, 0).asInt();
- }
- }
-
- static void loadRift(RiftDevice & device, const Json::Value & node) {
- if (node.isNull()) {
- return;
- }
- String eyeCups = node.get(KEY_EYECUP, "A").asCString();
- char c = eyeCups.GetSize() ? eyeCups.GetCharAt(0) : 'A';
- switch (eyeCups.GetCharAt(0)) {
- case 'A':
- device.EyeCups = EyeCupType::EyeCup_A;
- break;
- case 'B':
- device.EyeCups = EyeCupType::EyeCup_B;
- break;
- case 'C':
- device.EyeCups = EyeCupType::EyeCup_C;
- break;
- }
- loadHmd(device, node);
- }
-
- // TODO migrate all the string constants up
- static void loadV1Profile(Profile & out, const Json::Value & node) {
- if (node.isNull()) {
- return;
- }
- String gender = node.get(KEY_GENDER, "Unknown").asCString();
- if (gender == "Male") {
- out.Gender = Profile::GenderType::Gender_Male;
- } else if (gender == "Female") {
- out.Gender = Profile::GenderType::Gender_Female;
- } else {
- out.Gender = Profile::GenderType::Gender_Unspecified;
- }
-
- out.PlayerHeight = node.get(KEY_PLAYER_HEIGHT, DEFAULT_HEIGHT).asFloat();
- out.IPD = node.get(KEY_IPD, DEFAULT_IPD).asFloat();
- loadHmd(out.Generic, node.get("GenericHMD", Json::Value::null));
- loadRift(out.RiftDK1, node.get("RiftDK1", Json::Value::null));
- loadRift(out.RiftDKHD, node.get("RiftDKHD", Json::Value::null));
- }
-
- static void loadProfile(Profile & out, const Json::Value & node) {
- if (node.isNull()) {
- return;
- }
- loadV1Profile(out, node);
- if (node.isMember(KEY_STRABISMUS_CORRECTION)) {
- loadQuaternion(out.StrabismusCorrection, node[KEY_STRABISMUS_CORRECTION]);
- }
- }
-
- static void loadQuaternion(Quatf & out, const Json::Value & node) {
- if (node.isNull()) {
- return;
- }
- out.x = node.get("X", 0).asInt();
- out.y = node.get("Y", 0).asInt();
- out.z = node.get("Z", 0).asInt();
- out.w = node.get("W", 1).asInt();
- }
-
- static void saveQuaternion(Json::Value & out, const Quatf & q) {
- out["X"] = q.x;
- out["Y"] = q.y;
- out["Z"] = q.z;
- out["W"] = q.w;
- }
-
- // TODO implement, and migrate the Devices.json parsing code to use this
- static void loadMatrix(Matrix4f & out, const Json::Value & node) {
- }
-
- //
- // The general pattern on writing Json is to write the node only if
- // it differs from the default, and to explicitly remove the node if it
- // is the same as the default. This is important, because the load / save
- // mechanism is designed to preserve any pre-existing content and not
- // to touch any fields it's not aware of.
- //
- // Therefore, failing to write out a default value doesn't mean there's
- // not already a non-default value there, hence the explicit removes.
- // It gets a little verbose but the result is greater extensibility of the
- // profile data by third parties.
- static void writeHmdDevice(Json::Value & out, const HmdDevice & device) {
- if (device.LL != 0) {
- out[KEY_LL] = device.LL;
- } else {
- out.removeMember(KEY_LL);
- }
- if (device.LR != 0) {
- out[KEY_LR] = device.LR;
- } else {
- out.removeMember(KEY_LR);
- }
- if (device.RL != 0) {
- out[KEY_RL] = device.RL;
- } else {
- out.removeMember(KEY_RL);
- }
- if (device.RR != 0) {
- out[KEY_RR] = device.RR;
- } else {
- out.removeMember(KEY_RR);
- }
- }
-
- static void writeRiftDevice(Json::Value & deviceNode, const RiftDevice & device) {
- switch (device.EyeCups)
- {
- case EyeCup_B: deviceNode[KEY_EYECUP] = "B"; break;
- case EyeCup_C: deviceNode[KEY_EYECUP] = "C"; break;
- // A is the default, so no need to serialize it
- case EyeCup_A: deviceNode.removeMember(KEY_EYECUP); break;
- }
- writeHmdDevice(deviceNode, device);
- }
-
- // Applying the "remove nodes if they're empty" logic here is a little
- // arduous, but it makes the resulting JSON cleaner
- static void updateDeviceProfile(Json::Value & parent, const HmdDevice & device) {
- String name;
- Json::Value newChild;
- switch (device.GetDeviceType()) {
- case Profile_RiftDK1:
- name = "RiftDK1";
- newChild = parent[name];
- writeRiftDevice(newChild, (RiftDevice&)device);
- break;
-
- case Profile_RiftDKHD:
- name = "RiftDKHD";
- newChild = parent[name];
- writeRiftDevice(newChild, (RiftDevice&)device);
- break;
-
- case Profile_GenericHMD:
- name = "GenericHMD";
- newChild = parent[name];
- writeHmdDevice(newChild, device);
- break;
- }
-
- // Don't write empty children
- if (newChild.getMemberNames().size()) {
- parent[name] = newChild;
- } else {
- parent.removeMember(name);
- }
- }
-
- static void writeProfile(Json::Value & parent, const Profile & profile) {
- Json::Value & out = parent[profile.Name];
-
- switch (profile.GetGender()) {
- case Profile::Gender_Male:
- out[KEY_GENDER] = "Male";
- break;
- case Profile::Gender_Female:
- out[KEY_GENDER] = "Female";
- break;
- default:
- out.removeMember(KEY_GENDER);
- }
-
- // Epsilon is 10 micrometers. Smaller than a human hair
- if (abs(profile.PlayerHeight - DEFAULT_HEIGHT) > EPSILON) {
- out[KEY_PLAYER_HEIGHT] = profile.PlayerHeight;
- } else {
- out.removeMember(KEY_PLAYER_HEIGHT);
- }
-
- if (abs(profile.IPD - DEFAULT_IPD) > EPSILON) {
- out[KEY_IPD] = profile.IPD;
- } else {
- out.removeMember(KEY_IPD);
- }
-
- if (profile.StrabismusCorrection.Distance(Quatf()) > EPSILON) {
- saveQuaternion(out[KEY_STRABISMUS_CORRECTION],
- profile.StrabismusCorrection);
- } else {
- out.removeMember(KEY_STRABISMUS_CORRECTION);
- }
-
- Json::Value & devicesNode = out[KEY_DEVICES];
- updateDeviceProfile(devicesNode, profile.Generic);
- updateDeviceProfile(devicesNode, profile.RiftDK1);
- updateDeviceProfile(devicesNode, profile.RiftDKHD);
- if (!devicesNode.getMemberNames().size()) {
- out.removeMember(KEY_DEVICES);
- }
- }
-};
-
-// Use of STL IO is potentially avoidable with jsoncpp, if you wanted
-// to load the file into a String instead and use the char* interface for
-// parsing JSON in memory. Linking to C++ standard library is unavoidable
-// though, but it might not take much work to remove iostream usage from
-// the jsoncpp lib and make it use std::string only.
-bool parseJsonFile(const String & file, Json::Value & root) {
- Json::Reader reader;
- String path = GetProfilePath(false);
- std::ifstream in(path.ToCStr());
- if (!reader.parse(in, root)) {
- // report to the user the failure and their locations in the document.
- LogError("Failed to parse json file: %s\n %s",
- file.ToCStr(),
- reader.getFormattedErrorMessages().c_str());
- return false;
- }
- return true;
-
-}
// Poplulates the local profile cache. This occurs on the first access of the profile
// data. All profile operations are performed against the local cache until the
// ProfileManager is released or goes out of scope at which time the cache is serialized
// to disk.
-void ProfileManager::LoadCache() {
- if (Loaded) {
- return;
- }
-
- Lock::Locker lockScope(&ProfileLock);
- ClearCache();
-
- Json::Value root;
- if (!parseJsonFile(GetProfilePath(false), root)) {
- LogError("Failed to parse configuration");
- return;
- }
-
- if (!root.isMember(KEY_PROFILE_VERSION)) {
- LogError("Profile JSON is malformed, missing version number");
- return;
- }
-
- int major = root[KEY_PROFILE_VERSION].asInt();
- switch (major) {
- case 1: {
- if (root.size() < 3) {
- LogError("Profile JSON is malformed, insufficient keys");
+void ProfileManager::LoadCache(ProfileType device)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ ClearCache();
+
+ String path = GetProfilePath(false);
+
+ Ptr<JSON> root = *JSON::Load(path);
+ if (!root || root->GetItemCount() < 3)
return;
- }
- DefaultProfile = root[KEY_CURRENT_PROFILE].asCString();
- const Json::Value & profileNode = root["Profile"];
- String name = profileNode["Name"].asCString();
- // The profile having to know it's own name is a symptom of the lack
- // of a proper associative array class in the OVR codebase.
- Profile * profile = new Profile(name);
- ProfileLoader::loadV1Profile(*profile, profileNode);
- ProfileCache.PushBack(profile);
- }
- break;
-
- case 2: {
- if (!root.isMember(KEY_PROFILES)) {
- LogError("Missing profile data");
+
+ // First read the file type and version to make sure this is a valid file
+ JSON* item0 = root->GetFirstItem();
+ JSON* item1 = root->GetNextItem(item0);
+ JSON* item2 = root->GetNextItem(item1);
+
+ if (item0->Name == "Oculus Profile Version")
+ {
+ int major = atoi(item0->Value.ToCStr());
+ if (major > MAX_PROFILE_MAJOR_VERSION)
+ return; // don't parse the file on unsupported major version number
+ }
+ else
+ {
return;
- }
-
- const Json::Value & profiles = root[KEY_PROFILES];
- Json::Value::const_iterator itr;
- for (itr = profiles.begin(); itr != profiles.end(); ++itr) {
- String profileName = itr.memberName();
- Profile * profile = new Profile(profileName);
- ProfileLoader::loadProfile(*profile, root[profileName]);
- ProfileCache.PushBack(profile);
- }
-
- if (!root.isMember(KEY_CURRENT_PROFILE)) {
- LogError("Missing current profile");
- } else {
- DefaultProfile = root[KEY_CURRENT_PROFILE].asCString();
- }
- }
- break;
-
- default:
- LogError("Usupported profile version %d", major);
- return; // don't parse the file on unsupported major version number
- }
- Loaded = true;
+ }
+
+ DefaultProfile = item1->Value;
+
+ // Read the number of profiles
+ int profileCount = (int)item2->dValue;
+ JSON* profileItem = item2;
+
+ for (int p=0; p<profileCount; p++)
+ {
+ profileItem = root->GetNextItem(profileItem);
+ if (profileItem == NULL)
+ break;
+
+ if (profileItem->Name == "Profile")
+ {
+ // Read the required Name field
+ const char* profileName;
+ JSON* item = profileItem->GetFirstItem();
+
+ if (item && (item->Name == "Name"))
+ {
+ profileName = item->Value;
+ }
+ else
+ {
+ return; // invalid field
+ }
+
+ const char* deviceName = 0;
+ bool deviceFound = false;
+ Ptr<Profile> profile = *CreateProfileObject(profileName, device, &deviceName);
+
+ // Read the base profile fields.
+ if (profile)
+ {
+ while (item = profileItem->GetNextItem(item), item)
+ {
+ if (item->Type != JSON_Object)
+ {
+ profile->ParseProperty(item->Name, item->Value);
+ }
+ else
+ { // Search for the matching device to get device specific fields
+ if (!deviceFound && deviceName && OVR_strcmp(item->Name, deviceName) == 0)
+ {
+ deviceFound = true;
+
+ for (JSON* deviceItem = item->GetFirstItem(); deviceItem;
+ deviceItem = item->GetNextItem(deviceItem))
+ {
+ profile->ParseProperty(deviceItem->Name, deviceItem->Value);
+ }
+ }
+ }
+ }
+ }
+
+ // Add the new profile
+ ProfileCache.PushBack(profile);
+ }
+ }
+
+ CacheDevice = device;
}
+
// Serializes the profiles to disk.
void ProfileManager::SaveCache()
{
+ String path = GetProfilePath(true);
+
Lock::Locker lockScope(&ProfileLock);
- // Limit scope of reader
- Json::Value root;
- String path = GetProfilePath(false);
- if (!parseJsonFile(path, root))
+
+ Ptr<JSON> oldroot = *JSON::Load(path);
+ if (oldroot)
{
- root = Json::Value();
+ if (oldroot->GetItemCount() >= 3)
+ {
+ JSON* item0 = oldroot->GetFirstItem();
+ JSON* item1 = oldroot->GetNextItem(item0);
+ oldroot->GetNextItem(item1);
+
+ if (item0->Name == "Oculus Profile Version")
+ {
+ int major = atoi(item0->Value.ToCStr());
+ if (major > MAX_PROFILE_MAJOR_VERSION)
+ oldroot.Clear(); // don't use the file on unsupported major version number
+ }
+ else
+ {
+ oldroot.Clear();
+ }
+ }
+ else
+ {
+ oldroot.Clear();
+ }
}
+
+ // Create a new json root
+ Ptr<JSON> root = *JSON::CreateObject();
+ root->AddNumberItem("Oculus Profile Version", PROFILE_VERSION);
+ root->AddStringItem("CurrentProfile", DefaultProfile);
+ root->AddNumberItem("ProfileCount", (double) ProfileCache.GetSize());
- // If the file is V1, we don't want to preserve it.
- // Technically, we could make an effort to preserve it but explcitly
- // remove the V1 tokens we don't use in V2, but that's a bunch of
- // effort that will likely serve no useful purpose. Since the file
- // isn't valid JSON if it has multiple profiles, I doubt anyone is
- // extending it yet
- if (root.isMember(KEY_PROFILE_VERSION) &&
- PROFILE_VERSION != root[KEY_PROFILE_VERSION].asInt()) {
- root = Json::Value();
- }
+ // Generate a JSON subtree for each profile
+ for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
+ {
+ Profile* profile = ProfileCache[i];
+
+ // Write the base profile information
+ JSON* json_profile = JSON::CreateObject();
+ json_profile->Name = "Profile";
+ json_profile->AddStringItem("Name", profile->Name);
+ const char* gender;
+ switch (profile->GetGender())
+ {
+ case Profile::Gender_Male: gender = "Male"; break;
+ case Profile::Gender_Female: gender = "Female"; break;
+ default: gender = "Unspecified";
+ }
+ json_profile->AddStringItem("Gender", gender);
+ json_profile->AddNumberItem("PlayerHeight", profile->PlayerHeight);
+ json_profile->AddNumberItem("IPD", profile->IPD);
- root[KEY_PROFILE_VERSION] = PROFILE_VERSION;
- if (!DefaultProfile.IsEmpty()) {
- root[KEY_CURRENT_PROFILE] = DefaultProfile.ToCStr();
- }
+ char* device_name = NULL;
+ // Create a device-specific subtree for the cached device
+ if (profile->Type == Profile_RiftDK1)
+ {
+ device_name = "RiftDK1";
+
+ RiftDK1Profile* rift = (RiftDK1Profile*)profile;
+ JSON* json_rift = JSON::CreateObject();
+ json_profile->AddItem(device_name, json_rift);
+
+ const char* eyecup = "A";
+ switch (rift->EyeCups)
+ {
+ case EyeCup_A: eyecup = "A"; break;
+ case EyeCup_B: eyecup = "B"; break;
+ case EyeCup_C: eyecup = "C"; break;
+ }
+ json_rift->AddStringItem("EyeCup", eyecup);
+ json_rift->AddNumberItem("LL", rift->LL);
+ json_rift->AddNumberItem("LR", rift->LR);
+ json_rift->AddNumberItem("RL", rift->RL);
+ json_rift->AddNumberItem("RR", rift->RR);
+ }
+ else if (profile->Type == Profile_RiftDKHD)
+ {
+ device_name = "RiftDKHD";
+
+ RiftDKHDProfile* rift = (RiftDKHDProfile*)profile;
+ JSON* json_rift = JSON::CreateObject();
+ json_profile->AddItem(device_name, json_rift);
+
+ const char* eyecup = "A";
+ switch (rift->EyeCups)
+ {
+ case EyeCup_A: eyecup = "A"; break;
+ case EyeCup_B: eyecup = "B"; break;
+ case EyeCup_C: eyecup = "C"; break;
+ }
+ json_rift->AddStringItem("EyeCup", eyecup);
+ //json_rift->AddNumberItem("LL", rift->LL);
+ //json_rift->AddNumberItem("LR", rift->LR);
+ //json_rift->AddNumberItem("RL", rift->RL);
+ //json_rift->AddNumberItem("RR", rift->RR);
+ }
+
+ // There may be multiple devices stored per user, but only a single
+ // device is represented by this root. We don't want to overwrite
+ // the other devices so we need to examine the older root
+ // and merge previous devices into new json root
+ if (oldroot)
+ {
+ JSON* old_profile = oldroot->GetFirstItem();
+ while (old_profile)
+ {
+ if (old_profile->Name == "Profile")
+ {
+ JSON* profile_name = old_profile->GetItemByName("Name");
+ if (profile_name && OVR_strcmp(profile->Name, profile_name->Value) == 0)
+ { // Now that we found the user in the older root, add all the
+ // object children to the new root - except for the one for the
+ // current device
+ JSON* old_item = old_profile->GetFirstItem();
+ while (old_item)
+ {
+ if (old_item->Type == JSON_Object
+ && (device_name == NULL || OVR_strcmp(old_item->Name, device_name) != 0))
+ {
+ JSON* old_device = old_item;
+ old_item = old_profile->GetNextItem(old_item);
+
+ // remove the node from the older root to avoid multiple reference
+ old_device->RemoveNode();
+ // add the node pointer to the new root
+ json_profile->AddItem(old_device->Name, old_device);
+ }
+ else
+ {
+ old_item = old_profile->GetNextItem(old_item);
+ }
+ }
+
+ break;
+ }
+ }
+
+ old_profile = oldroot->GetNextItem(old_profile);
+ }
+ }
- // Generate a JSON object of 'profile name' to 'profile data'
- Json::Value & profiles = root[KEY_PROFILES];
- for (unsigned int i=0; i<ProfileCache.GetSize(); i++) {
- const Profile * profile = ProfileCache[i];
- ProfileLoader::writeProfile(profiles, *profile);
+ // Add the completed user profile to the new root
+ root->AddItem("Profile", json_profile);
}
+ // Save the profile to disk
+ root->Save(path);
+}
+
+// Returns the number of stored profiles for this device type
+int ProfileManager::GetProfileCount(ProfileType device)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
+
+ return (int)ProfileCache.GetSize();
+}
+
+// Returns the profile name of a specific profile in the list. The returned
+// memory is locally allocated and should not be stored or deleted. Returns NULL
+// if the index is invalid
+const char* ProfileManager::GetProfileName(ProfileType device, unsigned int index)
+{
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
+
+ if (index < ProfileCache.GetSize())
{
- Json::StyledWriter writer;
- std::string output = writer.write( root );
- SysFile f;
- if (!f.Open(path, File::Open_Write | File::Open_Create | File::Open_Truncate, File::Mode_Write)) {
- LogError("Unable to open %s for writing", path.ToCStr());
- return;
- }
- int written = f.Write((const unsigned char*)output.c_str(),
- output.length());
- if (written != output.length()) {
- LogError("Short write, only %d of %d bytes written",
- written, (int)output.length());
- }
- f.Close();
+ Profile* profile = ProfileCache[index];
+ OVR_strcpy(NameBuff, Profile::MaxNameLen, profile->Name);
+ return NameBuff;
+ }
+ else
+ {
+ return NULL;
}
}
-// Returns the number of stored profiles for this device type
-unsigned int ProfileManager::GetProfileCount()
+bool ProfileManager::HasProfile(ProfileType device, const char* name)
{
Lock::Locker lockScope(&ProfileLock);
- return ProfileCache.GetSize();
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
+
+ for (unsigned i = 0; i< ProfileCache.GetSize(); i++)
+ {
+ if (ProfileCache[i] && OVR_strcmp(ProfileCache[i]->Name, name) == 0)
+ return true;
+ }
+ return false;
}
-bool ProfileManager::HasProfile(const char* name)
+
+// Returns a specific profile object in the list. The returned memory should be
+// encapsulated in a Ptr<> object or released after use. Returns NULL if the index
+// is invalid
+Profile* ProfileManager::LoadProfile(ProfileType device, unsigned int index)
{
- return NULL != ProfileCache.at(name);
+ Lock::Locker lockScope(&ProfileLock);
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
+
+ if (index < ProfileCache.GetSize())
+ {
+ Profile* profile = ProfileCache[index];
+ return profile->Clone();
+ }
+ else
+ {
+ return NULL;
+ }
}
-// Returns a profile object for a particular device and user name. The returned
-// memory should be encapsulated in a Ptr<> object or released after use. Returns
+// Returns a profile object for a particular device and user name. The returned
+// memory should be encapsulated in a Ptr<> object or released after use. Returns
// NULL if the profile is not found
-Profile* ProfileManager::LoadProfile(const char* user)
+Profile* ProfileManager::LoadProfile(ProfileType device, const char* user)
{
- // Maybe 'null' should be interpreted as 'return the default?'
if (user == NULL)
return NULL;
Lock::Locker lockScope(&ProfileLock);
- LoadCache();
- Profile * result = ProfileCache.at(user);
- if (!result) {
- return NULL;
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
+
+ for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
+ {
+ if (OVR_strcmp(user, ProfileCache[i]->Name) == 0)
+ { // Found the requested user profile
+ Profile* profile = ProfileCache[i];
+ return profile->Clone();
+ }
}
- // Never give the caller memory that we ourselves are managing.
- return result->Clone();
+
+ return NULL;
}
// Returns a profile with all system default values
-Profile* ProfileManager::GetDefaultProfile()
+Profile* ProfileManager::GetDeviceDefaultProfile(ProfileType device)
{
- return new Profile("default");
+ const char* device_name = NULL;
+ return CreateProfileObject("default", device, &device_name);
}
// Returns the name of the profile that is marked as the current default user.
-const char* ProfileManager::GetDefaultProfileName()
+const char* ProfileManager::GetDefaultProfileName(ProfileType device)
{
Lock::Locker lockScope(&ProfileLock);
- LoadCache();
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
if (ProfileCache.GetSize() > 0)
{
- OVR_strcpy(NameBuff, 32, DefaultProfile);
+ OVR_strcpy(NameBuff, Profile::MaxNameLen, DefaultProfile);
return NameBuff;
}
else
@@ -607,12 +558,14 @@ const char* ProfileManager::GetDefaultProfileName()
}
}
-
// Marks a particular user as the current default user.
-bool ProfileManager::SetDefaultProfileName(const char* name) {
+bool ProfileManager::SetDefaultProfileName(ProfileType device, const char* name)
+{
Lock::Locker lockScope(&ProfileLock);
- LoadCache();
- // TODO: I should verify that the user is valid
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(device);
+// TODO: I should verify that the user is valid
if (ProfileCache.GetSize() > 0)
{
DefaultProfile = name;
@@ -628,67 +581,132 @@ bool ProfileManager::SetDefaultProfileName(const char* name) {
// Saves a new or existing profile. Returns true on success or false on an
// invalid or failed save.
-bool ProfileManager::Save(const Profile * profile)
+bool ProfileManager::Save(const Profile* profile)
{
- if (NULL == profile) {
- return false;
- }
+ Lock::Locker lockScope(&ProfileLock);
if (OVR_strcmp(profile->Name, "default") == 0)
return false; // don't save a default profile
- Lock::Locker lockScope(&ProfileLock);
- LoadCache();
+ // TODO: I should also verify that this profile type matches the current cache
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(profile->Type);
// Look for the pre-existence of this profile
- int index = ProfileCache.IndexOf(profile->Name);
- if (Map::npos == index) {
- // TODO: I should do a proper field comparison to avoid unnecessary
- // overwrites and file saves
+ bool added = false;
+ for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
+ {
+ int compare = OVR_strcmp(profile->Name, ProfileCache[i]->Name);
+
+ if (compare == 0)
+ {
+ // TODO: I should do a proper field comparison to avoid unnecessary
+ // overwrites and file saves
+
+ // Replace the previous instance with the new profile
+ ProfileCache[i] = *profile->Clone();
+ added = true;
+ Changed = true;
+ break;
+ }
+ }
+
+ if (!added)
+ {
+ ProfileCache.PushBack(*profile->Clone());
+ if (ProfileCache.GetSize() == 1)
+ CacheDevice = profile->Type;
- // Replace the previous instance with the new profile
- ProfileCache[index] = profile->Clone();
- } else {
- ProfileCache.PushBack(profile->Clone());
+ Changed = true;
}
- Changed = true;
+
return true;
}
// Removes an existing profile. Returns true if the profile was found and deleted
// and returns false otherwise.
-bool ProfileManager::Delete(const Profile * profile)
+bool ProfileManager::Delete(const Profile* profile)
{
- if (NULL == profile) {
- return false;
- }
Lock::Locker lockScope(&ProfileLock);
- int index = ProfileCache.IndexOf(profile->Name);
- if (Map::npos == index) {
- return false;
+
+ if (OVR_strcmp(profile->Name, "default") == 0)
+ return false; // don't delete a default profile
+
+ if (CacheDevice == Profile_Unknown)
+ LoadCache(profile->Type);
+
+ // Look for the existence of this profile
+ for (unsigned int i=0; i<ProfileCache.GetSize(); i++)
+ {
+ if (OVR_strcmp(profile->Name, ProfileCache[i]->Name) == 0)
+ {
+ if (OVR_strcmp(profile->Name, DefaultProfile) == 0)
+ DefaultProfile.Clear();
+
+ ProfileCache.RemoveAt(i);
+ Changed = true;
+ return true;
+ }
}
- ProfileCache.RemoveAt(index);
- return true;
+
+ return false;
}
+
+
//-----------------------------------------------------------------------------
// ***** Profile
-Profile::Profile(const char* name)
+Profile::Profile(ProfileType device, const char* name)
{
+ Type = device;
Gender = Gender_Unspecified;
- PlayerHeight = DEFAULT_HEIGHT;
- IPD = DEFAULT_IPD;
- Name = name;
+ PlayerHeight = 1.778f; // 5'10" inch man
+ IPD = 0.064f;
+ OVR_strcpy(Name, MaxNameLen, name);
+}
+
+
+bool Profile::ParseProperty(const char* prop, const char* sval)
+{
+ if (OVR_strcmp(prop, "Name") == 0)
+ {
+ OVR_strcpy(Name, MaxNameLen, sval);
+ return true;
+ }
+ else if (OVR_strcmp(prop, "Gender") == 0)
+ {
+ if (OVR_strcmp(sval, "Male") == 0)
+ Gender = Gender_Male;
+ else if (OVR_strcmp(sval, "Female") == 0)
+ Gender = Gender_Female;
+ else
+ Gender = Gender_Unspecified;
+
+ return true;
+ }
+ else if (OVR_strcmp(prop, "PlayerHeight") == 0)
+ {
+ PlayerHeight = (float)atof(sval);
+ return true;
+ }
+ else if (OVR_strcmp(prop, "IPD") == 0)
+ {
+ IPD = (float)atof(sval);
+ return true;
+ }
+
+ return false;
}
+
// Computes the eye height from the metric head height
-float Profile::GetEyeHeight() const
+float Profile::GetEyeHeight()
{
const float EYE_TO_HEADTOP_RATIO = 0.44538f;
const float MALE_AVG_HEAD_HEIGHT = 0.232f;
const float FEMALE_AVG_HEAD_HEIGHT = 0.218f;
-
+
// compute distance from top of skull to the eye
float head_height;
if (Gender == Gender_Female)
@@ -702,4 +720,107 @@ float Profile::GetEyeHeight() const
return eye_height;
}
+//-----------------------------------------------------------------------------
+// ***** HMDProfile
+
+HMDProfile::HMDProfile(ProfileType type, const char* name) : Profile(type, name)
+{
+ LL = 0;
+ LR = 0;
+ RL = 0;
+ RR = 0;
+}
+
+bool HMDProfile::ParseProperty(const char* prop, const char* sval)
+{
+ if (OVR_strcmp(prop, "LL") == 0)
+ {
+ LL = atoi(sval);
+ return true;
+ }
+ else if (OVR_strcmp(prop, "LR") == 0)
+ {
+ LR = atoi(sval);
+ return true;
+ }
+ else if (OVR_strcmp(prop, "RL") == 0)
+ {
+ RL = atoi(sval);
+ return true;
+ }
+ else if (OVR_strcmp(prop, "RR") == 0)
+ {
+ RR = atoi(sval);
+ return true;
+ }
+
+ return Profile::ParseProperty(prop, sval);
+}
+
+Profile* HMDProfile::Clone() const
+{
+ HMDProfile* profile = new HMDProfile(*this);
+ return profile;
+}
+
+//-----------------------------------------------------------------------------
+// ***** RiftDK1Profile
+
+RiftDK1Profile::RiftDK1Profile(const char* name) : HMDProfile(Profile_RiftDK1, name)
+{
+ EyeCups = EyeCup_A;
+}
+
+bool RiftDK1Profile::ParseProperty(const char* prop, const char* sval)
+{
+ if (OVR_strcmp(prop, "EyeCup") == 0)
+ {
+ switch (sval[0])
+ {
+ case 'C': EyeCups = EyeCup_C; break;
+ case 'B': EyeCups = EyeCup_B; break;
+ default: EyeCups = EyeCup_A; break;
+ }
+ return true;
+ }
+
+ return HMDProfile::ParseProperty(prop, sval);
+}
+
+Profile* RiftDK1Profile::Clone() const
+{
+ RiftDK1Profile* profile = new RiftDK1Profile(*this);
+ return profile;
+}
+
+//-----------------------------------------------------------------------------
+// ***** RiftDKHDProfile
+
+RiftDKHDProfile::RiftDKHDProfile(const char* name) : HMDProfile(Profile_RiftDKHD, name)
+{
+ EyeCups = EyeCup_A;
+}
+
+bool RiftDKHDProfile::ParseProperty(const char* prop, const char* sval)
+{
+ if (OVR_strcmp(prop, "EyeCup") == 0)
+ {
+ switch (sval[0])
+ {
+ case 'C': EyeCups = EyeCup_C; break;
+ case 'B': EyeCups = EyeCup_B; break;
+ default: EyeCups = EyeCup_A; break;
+ }
+ return true;
+ }
+
+ return HMDProfile::ParseProperty(prop, sval);
+}
+
+Profile* RiftDKHDProfile::Clone() const
+{
+ RiftDKHDProfile* profile = new RiftDKHDProfile(*this);
+ return profile;
+}
+
} // OVR
diff --git a/LibOVR/Src/OVR_Profile.h b/LibOVR/Src/OVR_Profile.h
index 2c5e4c2..df25fea 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,12 +25,11 @@ 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 ProfileDeviceType
+enum ProfileType
{
Profile_Unknown = 0,
Profile_GenericHMD = 10,
@@ -38,17 +37,119 @@ enum ProfileDeviceType
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:
//
-// HMDProfile and it's child classes, RiftProfile, RiftDk1Profile and
-// RiftDKHDProfile represent the intersection of 'per-user' and 'per-device'
-// settings.
-//
+// {
+// Ptr<ProfileManager> pm = *ProfileManager::Create();
+// Ptr<Profile> 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<ProfileManager>
+{
+protected:
+ // Synchronize ProfileManager access since it may be accessed from multiple threads,
+ // as it's shared through DeviceManager.
+ Lock ProfileLock;
+ Array<Ptr<Profile> > 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<Profile>
+{
+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
// The generic HMD profile is used for properties that are common to all headsets
-class HmdDevice
+class HMDProfile : public Profile
{
protected:
// FOV extents in pixels measured by a user
@@ -58,7 +159,8 @@ protected:
int RR; // right eye outer extent
public:
- virtual ~HmdDevice() {}
+ virtual Profile* Clone() const;
+
void SetLL(int val) { LL = val; };
void SetLR(int val) { LR = val; };
void SetRL(int val) { RL = val; };
@@ -68,19 +170,13 @@ public:
int GetLR() { return LR; };
int GetRL() { return RL; };
int GetRR() { return RR; };
- virtual ProfileDeviceType GetDeviceType() const {
- return Profile_GenericHMD;
- }
protected:
- HmdDevice() {
- LL = 0;
- LR = 0;
- RL = 0;
- RR = 0;
- }
- friend class ProfileLoader;
- friend class Profile;
+ HMDProfile(ProfileType type, const char* name);
+
+ virtual bool ParseProperty(const char* prop, const char* sval);
+
+ friend class ProfileManager;
};
// For headsets that use eye cups
@@ -91,259 +187,52 @@ enum EyeCupType
EyeCup_C = 2
};
-class RiftDevice : public HmdDevice {
+//-----------------------------------------------------------------------------
+// ***** 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
+{
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:
- RiftDevice() {
- EyeCups = EyeCup_A;
- }
- friend class ProfileLoader;
- friend class Profile;
-};
+ RiftDK1Profile(const char* name);
-//-----------------------------------------------------------------------------
-// ***** RiftDK1Profile
-
-// 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;
- }
+ virtual bool ParseProperty(const char* prop, const char* sval);
-protected:
- RiftDK1Device() {
-
- }
- friend class ProfileLoader;
- friend class Profile;
+ friend class ProfileManager;
};
//-----------------------------------------------------------------------------
// ***** 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 RiftDKHDDevice : public RiftDevice
+class RiftDKHDProfile : public HMDProfile
{
-public:
- virtual ProfileDeviceType GetDeviceType() const {
- return Profile_RiftDKHD;
- }
-
protected:
- RiftDKHDDevice() {
- }
- friend class ProfileLoader;
- friend class Profile;
-};
+ EyeCupType EyeCups; // Which eye cup does the player use
-template <class KeyType>
-class KeyedObject {
public:
- virtual ~KeyedObject() {
- }
- virtual const KeyType & GetKey() const = 0;
-};
-//-------------------------------------------------------------------
-// ***** Profile
+ virtual Profile* Clone() const;
-// The base profile for all users. This object is not created directly.
-//
-class Profile : public RefCountBase<Profile>, KeyedObject<String>
-{
-public:
- enum GenderType
- {
- Gender_Unspecified = 0,
- Gender_Male = 1,
- Gender_Female = 2
- };
-
- String Name; // The name given to this profile
+ EyeCupType GetEyeCup() { return EyeCups; };
+ void SetEyeCup(EyeCupType cup) { EyeCups = cup; };
protected:
- 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;
+ RiftDKHDProfile(const char* name);
-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);
- }
+ virtual bool ParseProperty(const char* prop, const char* sval);
-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 ValueType, class KeyType>
-class AssociativePtrArray : public Array<Ptr<ValueType> > {
-public:
- enum {
- npos = -1
- };
-
- int IndexOf(const KeyType & key) const {
- for (int i = 0; i< this->GetSize(); ++i) {
- const Ptr<ValueType> & 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<ValueType>();
- }
- Ptr<ValueType> & ptr = this->Data.Data[index];
- return ptr;
- }
-
- const ValueType * at(const KeyType & key) const {
- int index = IndexOf(key);
- if (npos == index) {
- return Ptr<ValueType>();
- }
- const Ptr<ValueType> & 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<ProfileManager> pm = *ProfileManager::Create();
-// Ptr<Profile> 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<ProfileManager>
-{
-protected:
- // Synchronize ProfileManager access since it may be accessed from multiple threads,
- // as it's shared through DeviceManager.
- Lock ProfileLock;
- typedef AssociativePtrArray<Profile, String> 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();
};
@@ -351,4 +240,4 @@ String GetBaseOVRPath(bool create_dir);
}
-#endif // OVR_Profile_h
+#endif // OVR_Profile_h \ No newline at end of file