diff --git a/.gitmodules b/.gitmodules index ca92468..cc60014 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "modules/cgmath"] path = modules/cgmath url = https://github.com/bjz/cgmath-rs -[submodule "modules/OculusSDK"] - path = modules/OculusSDK - url = https://github.com/jherico/OculusSDK.git diff --git a/configure b/configure index 07e443a..982116e 100755 --- a/configure +++ b/configure @@ -7,18 +7,39 @@ import os.path import platform class Module: + def set_source_dir(self, source_dir): + self.source_dir = source_dir + + def set_output_dir(self, output_dir): + self.output_dir = output_dir + + def get_base_dir(self): + return self.source_dir + + def get_output_dir(self): + return os.path.join(self.output_dir, self.dir) + + def get_source_dir(self): + return os.path.join(self.get_base_dir(), "src") + + def get_source_crate(self): + return os.path.join(self.get_source_dir(), self.name, self.ext) + + def get_test_dir(self): + return os.path.join(self.get_source_dir(), self.name, "test.rs") + def has_tests(self): - return os.path.isfile("src/%s/test.rs" % self.name) + return os.path.isfile(self.get_test_dir()) def get_dep(self): - args = ["rustc", "src/%s/%s" % (self.name, self.ext), - "--dep-info", ".tmp.txt", "--no-analysis", "--no-trans", "--out-dir=%s" % self.dir] + args = ["rustc", self.get_source_crate(), + "--dep-info", ".tmp.txt", "--no-analysis", "--no-trans", "--out-dir=%s" % self.get_output_dir()] subprocess.call(args) with open(".tmp.txt", "r") as f: return f.read().split("\n")[0] def get_name(self): - args = ["rustc", "--crate-file-name", "src/%s/%s" % (self.name, self.ext)] + args = ["rustc", "--crate-file-name", self.get_source_crate()] with open(".tmp.txt", "w+") as f: subprocess.call(args, stdout=f) f.seek(0) @@ -36,19 +57,42 @@ class Module: def make_rule(self, mods): dep = self.get_dep() + " " - dep += " ".join(mods[m].ename for m in self.dep_modules) - how = "%s\n\trustc $(RUST_FLAGS) --out-dir=%s %s src/%s/%s\n" % (dep, self.dir, self.get_flags(mods), self.name, self.ext) + dep += " ".join(mods[m].get_ename() for m in self.dep_modules) + setup = "" + if self.setup: + setup = "\tsh -c \"%s\"\n" % self.setup + how = "%s\n%s\trustc --out-dir=%s %s %s\n" % ( + dep, setup, self.get_output_dir(), self.get_flags(mods), self.get_source_crate() + ) return how def make_test_rules(self, mods): dep = self.get_dep() + " " - dep += " ".join(mods[m].ename for m in self.dep_modules) - dep = ": ".join(["test/%s" % self.name, " ".join([self.ename, "src/%s/test.rs" % self.name, dep.split(": ", 2)[1]])]) - how = "%s\n\trustc $(RUST_FLAGS) --test -o test/%s %s src/%s/test.rs\n" % (dep, self.name, self.get_flags(mods), self.name) + dep += " ".join(mods[m].get_ename() for m in self.dep_modules) + dep = ": ".join(["test/%s" % self.name, " ".join( + [self.get_ename(), os.path.join(self.get_source_dir()), self.get_test_dir(), dep.split(": ", 2)[1]])] + ) + how = "%s\n\trustc --test -o test/%s %s %s\n" % ( + dep, self.name, self.get_flags(mods), self.get_test_dir() + ) how += "\ntest/.%s.check: test/%s\n" % (self.name, self.name) how += "\t./test/%s && touch test/.%s.check\n" % (self.name, self.name) return how + def pre_setup(self): + if self.presetup: + p = subprocess.Popen(["sh", "-c", self.presetup]) + p.wait() + + def write_cleanup(self, f): + pass + + def get_ename(self): + if self.ename == None: + self.ename = os.path.join(self.dir, self.get_name()) + return self.ename + + class cd: """Context manager for changing the current working directory, creating if necessary""" def __init__(self, newPath): @@ -68,89 +112,193 @@ class Lib(Module): ext = "lib.rs" dir = "lib" flags = "" - def __init__(self, name, dep_modules=None, other_flags=""): + def __init__(self, name, dep_modules=None, other_flags="", setup=None, presetup=None): + self.source_dir = "" self.name = name - self.ename = "lib/%s" % self.get_name() + self.ename = None self.other_flags = other_flags + self.setup = setup + self.presetup = presetup if dep_modules: self.dep_modules = dep_modules else: self.dep_modules = [] + def get_flags(self, mods): + flags = ["$(RUST_LIB_FLAGS)", self.flags] + self.collect_flags(mods) + return " ".join(flags) + class Bin(Module): ext = "main.rs" dir = "bin" - flags = "-Zlto" - def __init__(self, name, dep_modules=None, other_flags=""): + flags = "" + + def __init__(self, name, dep_modules=None, other_flags="", setup=None, presetup=None): + self.source_dir = "" self.name = name - self.ename = "bin/%s" % self.get_name() + self.ename = None self.other_flags = other_flags - if dep_modules: + self.setup = setup + self.presetup = presetup + if dep_modules: self.dep_modules = dep_modules else: self.dep_modules = [] -class LibMakefile(Module): + def get_flags(self, mods): + flags = ["$(RUST_BIN_FLAGS)", self.flags] + self.collect_flags(mods) + return " ".join(flags) + +class LibXcodebuild(Module): ext = "" - dir = "" + dir = "lib" flags = "" def get_name(self): return self.name - def __init__(self, name, path_to_makefile_dir, path_to_output, dep_modules=None, other_flags=""): + def get_path_to_xcode_project(self): + return os.path.join(self.get_base_dir(), self.path_to_xcode_project) + + def get_path_to_output_dir(self): + return " ".join(os.path.join(self.get_base_dir(), p) for p in self.path_to_output) + + def __init__(self, name, path_to_xcode_project, path_to_output, dep_modules=None, other_flags=""): + self.source_dir = "" self.name = name - self.ename = "lib/%s" % self.get_name() + self.ename = None self.other_flags = other_flags - self.path_to_makefile_dir = path_to_makefile_dir + self.path_to_xcode_project = path_to_xcode_project self.path_to_output = path_to_output + self.setup = None + self.presetup = None if dep_modules: self.dep_modules = dep_modules else: self.dep_modules = [] def make_rule(self, mods): - out = "%s: %s\n" % (self.ename, self.path_to_makefile_dir + "Makefile") - out += "\tmake -C %s && cp %s %s\n" % (self.path_to_makefile_dir, self.path_to_output, self.ename) + out = "%s: %s\n" % (self.get_ename(), + " ".join(mods[m].get_ename() for m in self.dep_modules)) + out += "\txcodebuild -project %s build\n\tcp %s lib\n" % ( + self.get_path_to_xcode_project(), self.get_path_to_output_dir() + ) return out -class LibCMake(Module): + def write_cleanup(self, f): + f.write("\t-xcodebuild -project %s clean\n" % self.get_path_to_xcode_project()) + +class LibMakefile(Module): ext = "" - dir = "" + dir = "lib" flags = "" def get_name(self): return self.name - def __init__(self, name, path_to_makefile_dir, path_to_output, dep_modules=None, other_flags="", cmake_flags=""): + def get_path_to_makefile_dir(self): + return os.path.join(self.get_base_dir(), self.path_to_makefile_dir) + + def get_path_to_output_dir(self): + return " ".join(os.path.join(self.get_base_dir(), p) for p in self.path_to_output) + + def __init__(self, name, path_to_makefile_dir, path_to_output, dep_modules=None, other_flags=""): + self.source_dir = "" self.name = name - self.ename = "lib/%s" % self.get_name() + self.ename = None + self.other_flags = other_flags + self.path_to_makefile_dir = path_to_makefile_dir + self.path_to_output = path_to_output + self.setup = None + self.presetup = None + if dep_modules: + self.dep_modules = dep_modules + else: + self.dep_modules = [] + + def make_rule(self, mods): + out = "%s: %s %s\n" % (self.get_ename(), + self.get_path_to_makefile_dir() + "Makefile", + " ".join(mods[m].get_ename() for m in self.dep_modules)) + out += "\tmake -j 16 -C %s\n\tcp %s lib\n" % ( + self.get_path_to_makefile_dir(), self.get_path_to_output_dir() + ) + return out + + def write_cleanup(self, f): + f.write("\t-make -C %s clean\n" % self.path_to_makefile_dir) + +class LibConfigureMakefile(LibMakefile): + def make_rule(self, mods): + out = "%s:\n" % (os.path.join(self.get_path_to_makefile_dir(), "Makefile")) + out += "\tcd %s && ./configure\n\n" % ( + os.path.join(self.get_path_to_makefile_dir()) + ) + out += "%s: %s\n" % (self.get_ename(), os.path.join(self.get_path_to_makefile_dir(), "Makefile")) + out += "\tmake -j 16 -C %s\n\tcp %s lib\n" % ( + self.get_path_to_makefile_dir(), self.get_path_to_output_dir() + ) + return out + +class LibCMake(Module): + ext = "" + dir = "lib" + flags = "" + + def get_name(self): + return self.name + + def get_path_to_makefile_dir(self): + return os.path.join(self.get_base_dir(), self.path_to_makefile_dir) + + def get_path_to_output_dir(self): + return " ".join(os.path.join(self.get_base_dir(), p) for p in self.path_to_output) + + def __init__(self, name, path_to_makefile_dir, path_to_output, dep_modules=None, other_flags="", cmake_flags=""): + self.source_dir = "" + self.name = name + self.ename = None self.other_flags = other_flags self.path_to_makefile_dir = path_to_makefile_dir self.path_to_output = path_to_output self.cmake_flags = cmake_flags + self.setup = None + self.presetup = None if dep_modules: self.dep_modules = dep_modules else: self.dep_modules = [] def make_rule(self, mods): - out = "%s:\n" % (self.path_to_makefile_dir + "Makefile") - out += "\tcd %s && cmake %s .\n\n" % (self.path_to_makefile_dir, self.cmake_flags) - out += "%s: %s\n" % (self.ename, self.path_to_makefile_dir + "Makefile") - out += "\tmake -C %s && cp %s %s\n" % (self.path_to_makefile_dir, self.path_to_output, self.ename) + out = "%s:\n" % (self.get_path_to_makefile_dir() + "Makefile") + out += "\tcd %s && cmake %s .\n\n" % (self.get_path_to_makefile_dir(), self.cmake_flags) + out += "%s: %s %s\n" % (self.get_ename(), + self.get_path_to_makefile_dir() + "Makefile", + " ".join(mods[m].get_ename() for m in self.dep_modules)) + out += "\tmake -j 16 -C %s && cp %s lib\n" % ( + self.get_path_to_makefile_dir(), self.get_path_to_output_dir() + ) return out + def write_cleanup(self, f): + f.write("\t-make -C %s clean\n" % self.path_to_makefile_dir) + f.write("\t-rm %s\n" % (os.path.join(self.path_to_makefile_dir, "Makefile"))) def write_makefile(modules): modules = {m.name: m for m in modules} + for m in modules.values(): + m.pre_setup() + rules = "\n".join(m.make_rule(modules) for m in modules.values()) + "\n" rules += "\n".join(m.make_test_rules(modules) for m in modules.values() if m.has_tests()) - all = " ".join(m.ename for m in modules.values()) + all = " ".join(m.get_ename() for m in modules.values()) with open("Makefile", "w+") as f: - f.write("RUST_FLAGS=--opt-level=3 -L lib\n") + f.write("RUST_FLAGS=-L lib --opt-level=3\n") + f.write("RUST_LIB_FLAGS=$(RUST_FLAGS)\n") + f.write("RUST_BIN_FLAGS=$(RUST_FLAGS) -Zlto\n") + f.write("RUST_TEST_FLAGS=$(RUST_FLAGS)\n") f.write("\n") f.write("all: lib bin test %s\n" % all) f.write("\n") @@ -164,10 +312,21 @@ def write_makefile(modules): f.write("\n") f.write("test/.check: lib test %s\n" % " ".join("test/.%s.check" % m.name for m in modules.values() if m.has_tests())) f.write("\n") - f.write("clean:\n\trm -r lib bin test\n") + f.write("clean:\n\t-rm -r lib bin test\n") + for m in modules.values(): + m.write_cleanup(f) f.write("\n") f.write(rules) +def set_output_dir(modules, output_dir): + for m in modules: + m.set_output_dir(output_dir) + +def set_source_dir(modules, source_dir): + for m in modules: + m.set_source_dir(source_dir) + +_base = os.path.abspath(os.path.dirname(__file__)) modules = [Bin("oculus-info", ["oculus-vr"]), Lib("cgmath")] @@ -179,6 +338,13 @@ if platform.system() == "Linux": "modules/OculusSDK/output/libOVR_C.so")] elif platform.system() == "Darwin": - modules += [Lib("oculus-vr", ["libovr.a", "cgmath"])] + modules += [Lib("oculus-vr", ["libovr.a", "cgmath"]), + LibXcodebuild("libovr.a", + "modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj", + ["modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a"])] -write_makefile(modules) \ No newline at end of file +set_output_dir(modules, ".") +set_source_dir(modules, _base) + +if __name__ == "__main__": + write_makefile(modules) \ No newline at end of file diff --git a/modules/OculusSDK b/modules/OculusSDK deleted file mode 160000 index ccd12dd..0000000 --- a/modules/OculusSDK +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ccd12dd9b28d52190d11267a937124eff586b89d diff --git a/modules/oculus_sdk_mac/.DS_Store b/modules/oculus_sdk_mac/.DS_Store new file mode 100644 index 0000000..6f22a25 Binary files /dev/null and b/modules/oculus_sdk_mac/.DS_Store differ diff --git a/modules/oculus_sdk_mac/3rdParty/.DS_Store b/modules/oculus_sdk_mac/3rdParty/.DS_Store new file mode 100644 index 0000000..412b909 Binary files /dev/null and b/modules/oculus_sdk_mac/3rdParty/.DS_Store differ diff --git a/modules/oculus_sdk_mac/3rdParty/TinyXml/tinyxml2.cpp b/modules/oculus_sdk_mac/3rdParty/TinyXml/tinyxml2.cpp new file mode 100644 index 0000000..00a6f3a --- /dev/null +++ b/modules/oculus_sdk_mac/3rdParty/TinyXml/tinyxml2.cpp @@ -0,0 +1 @@ +/* Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "tinyxml2.h" #include // yes, this one new style header, is in the Android SDK. # ifdef ANDROID_NDK # include #else # include #endif static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF static const char LF = LINE_FEED; static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out static const char CR = CARRIAGE_RETURN; static const char SINGLE_QUOTE = '\''; static const char DOUBLE_QUOTE = '\"'; // Bunch of unicode info at: // http://www.unicode.org/faq/utf_bom.html // ef bb bf (Microsoft "lead bytes") - designates UTF-8 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; #define DELETE_NODE( node ) { \ if ( node ) { \ MemPool* pool = node->_memPool; \ node->~XMLNode(); \ pool->Free( node ); \ } \ } #define DELETE_ATTRIBUTE( attrib ) { \ if ( attrib ) { \ MemPool* pool = attrib->_memPool; \ attrib->~XMLAttribute(); \ pool->Free( attrib ); \ } \ } namespace tinyxml2 { struct Entity { const char* pattern; int length; char value; }; static const int NUM_ENTITIES = 5; static const Entity entities[NUM_ENTITIES] = { { "quot", 4, DOUBLE_QUOTE }, { "amp", 3, '&' }, { "apos", 4, SINGLE_QUOTE }, { "lt", 2, '<' }, { "gt", 2, '>' } }; StrPair::~StrPair() { Reset(); } void StrPair::Reset() { if ( _flags & NEEDS_DELETE ) { delete [] _start; } _flags = 0; _start = 0; _end = 0; } void StrPair::SetStr( const char* str, int flags ) { Reset(); size_t len = strlen( str ); _start = new char[ len+1 ]; memcpy( _start, str, len+1 ); _end = _start + len; _flags = flags | NEEDS_DELETE; } char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) { TIXMLASSERT( endTag && *endTag ); char* start = p; // fixme: hides a member char endChar = *endTag; size_t length = strlen( endTag ); // Inner loop of text parsing. while ( *p ) { if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { Set( start, p, strFlags ); return p + length; } ++p; } return 0; } char* StrPair::ParseName( char* p ) { char* start = p; if ( !start || !(*start) ) { return 0; } while( *p && ( XMLUtil::IsAlphaNum( (unsigned char) *p ) || *p == '_' || *p == ':' || (*p == '-' && p>start ) // can be in a name, but not lead it. || (*p == '.' && p>start ) )) { // can be in a name, but not lead it. ++p; } if ( p > start ) { Set( start, p, 0 ); return p; } return 0; } void StrPair::CollapseWhitespace() { // Trim leading space. _start = XMLUtil::SkipWhiteSpace( _start ); if ( _start && *_start ) { char* p = _start; // the read pointer char* q = _start; // the write pointer while( *p ) { if ( XMLUtil::IsWhiteSpace( *p )) { p = XMLUtil::SkipWhiteSpace( p ); if ( *p == 0 ) { break; // don't write to q; this trims the trailing space. } *q = ' '; ++q; } *q = *p; ++q; ++p; } *q = 0; } } const char* StrPair::GetStr() { if ( _flags & NEEDS_FLUSH ) { *_end = 0; _flags ^= NEEDS_FLUSH; if ( _flags ) { char* p = _start; // the read pointer char* q = _start; // the write pointer while( p < _end ) { if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { // CR-LF pair becomes LF // CR alone becomes LF // LF-CR becomes LF if ( *(p+1) == LF ) { p += 2; } else { ++p; } *q++ = LF; } else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { if ( *(p+1) == CR ) { p += 2; } else { ++p; } *q++ = LF; } else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { // Entities handled by tinyXML2: // - special entities in the entity table [in/out] // - numeric character reference [in] // 中 or 中 if ( *(p+1) == '#' ) { char buf[10] = { 0 }; int len; p = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); for( int i=0; i(p); // Check for BOM: if ( *(pu+0) == TIXML_UTF_LEAD_0 && *(pu+1) == TIXML_UTF_LEAD_1 && *(pu+2) == TIXML_UTF_LEAD_2 ) { *bom = true; p += 3; } return p; } void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) { const unsigned long BYTE_MASK = 0xBF; const unsigned long BYTE_MARK = 0x80; const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; if (input < 0x80) { *length = 1; } else if ( input < 0x800 ) { *length = 2; } else if ( input < 0x10000 ) { *length = 3; } else if ( input < 0x200000 ) { *length = 4; } else { *length = 0; // This code won't covert this correctly anyway. return; } output += *length; // Scary scary fall throughs. switch (*length) { case 4: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 3: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 2: --output; *output = (char)((input | BYTE_MARK) & BYTE_MASK); input >>= 6; case 1: --output; *output = (char)(input | FIRST_BYTE_MARK[*length]); default: break; } } const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) { // Presume an entity, and pull it out. *length = 0; if ( *(p+1) == '#' && *(p+2) ) { unsigned long ucs = 0; ptrdiff_t delta = 0; unsigned mult = 1; if ( *(p+2) == 'x' ) { // Hexadecimal. if ( !*(p+3) ) { return 0; } const char* q = p+3; q = strchr( q, ';' ); if ( !q || !*q ) { return 0; } delta = q-p; --q; while ( *q != 'x' ) { if ( *q >= '0' && *q <= '9' ) { ucs += mult * (*q - '0'); } else if ( *q >= 'a' && *q <= 'f' ) { ucs += mult * (*q - 'a' + 10); } else if ( *q >= 'A' && *q <= 'F' ) { ucs += mult * (*q - 'A' + 10 ); } else { return 0; } mult *= 16; --q; } } else { // Decimal. if ( !*(p+2) ) { return 0; } const char* q = p+2; q = strchr( q, ';' ); if ( !q || !*q ) { return 0; } delta = q-p; --q; while ( *q != '#' ) { if ( *q >= '0' && *q <= '9' ) { ucs += mult * (*q - '0'); } else { return 0; } mult *= 10; --q; } } // convert the UCS to UTF-8 ConvertUTF32ToUTF8( ucs, value, length ); return p + delta + 1; } return p+1; } void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); } void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); } void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); } void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%g", v ); } void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) { TIXML_SNPRINTF( buffer, bufferSize, "%g", v ); } bool XMLUtil::ToInt( const char* str, int* value ) { if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) { if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToBool( const char* str, bool* value ) { int ival = 0; if ( ToInt( str, &ival )) { *value = (ival==0) ? false : true; return true; } if ( StringEqual( str, "true" ) ) { *value = true; return true; } else if ( StringEqual( str, "false" ) ) { *value = false; return true; } return false; } bool XMLUtil::ToFloat( const char* str, float* value ) { if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { return true; } return false; } bool XMLUtil::ToDouble( const char* str, double* value ) { if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { return true; } return false; } char* XMLDocument::Identify( char* p, XMLNode** node ) { XMLNode* returnNode = 0; char* start = p; p = XMLUtil::SkipWhiteSpace( p ); if( !p || !*p ) { return p; } // What is this thing? // - Elements start with a letter or underscore, but xml is reserved. // - Comments: // // With a special case: // // // // // Where the closing element (/foo) *must* be the next thing after the opening // element, and the names must match. BUT the tricky bit is that the closing // element will be read by the child. // // 'endTag' is the end tag for this node, it is returned by a call to a child. // 'parentEnd' is the end tag for the parent, which is filled in and returned. while( p && *p ) { XMLNode* node = 0; p = _document->Identify( p, &node ); if ( p == 0 || node == 0 ) { break; } StrPair endTag; p = node->ParseDeep( p, &endTag ); if ( !p ) { DELETE_NODE( node ); node = 0; if ( !_document->Error() ) { _document->SetError( XML_ERROR_PARSING, 0, 0 ); } break; } // We read the end tag. Return it to the parent. if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) { if ( parentEnd ) { *parentEnd = static_cast(node)->_value; } node->_memPool->SetTracked(); // created and then immediately deleted. DELETE_NODE( node ); return p; } // Handle an end tag returned to this level. // And handle a bunch of annoying errors. XMLElement* ele = node->ToElement(); if ( ele ) { if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); p = 0; } else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); p = 0; } else if ( !endTag.Empty() ) { if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); p = 0; } } } if ( p == 0 ) { DELETE_NODE( node ); node = 0; } if ( node ) { this->InsertEndChild( node ); } } return 0; } // --------- XMLText ---------- // char* XMLText::ParseDeep( char* p, StrPair* ) { const char* start = p; if ( this->CData() ) { p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); } return p; } else { int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { flags |= StrPair::COLLAPSE_WHITESPACE; } p = _value.ParseText( p, "<", flags ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); } if ( p && *p ) { return p-1; } } return 0; } XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? text->SetCData( this->CData() ); return text; } bool XMLText::ShallowEqual( const XMLNode* compare ) const { return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() )); } bool XMLText::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLComment ---------- // XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) { } XMLComment::~XMLComment() { } char* XMLComment::ParseDeep( char* p, StrPair* ) { // Comment parses as text. const char* start = p; p = _value.ParseText( p, "-->", StrPair::COMMENT ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); } return p; } XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? return comment; } bool XMLComment::ShallowEqual( const XMLNode* compare ) const { return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() )); } bool XMLComment::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLDeclaration ---------- // XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) { } XMLDeclaration::~XMLDeclaration() { //printf( "~XMLDeclaration\n" ); } char* XMLDeclaration::ParseDeep( char* p, StrPair* ) { // Declaration parses as text. const char* start = p; p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); if ( p == 0 ) { _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); } return p; } XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? return dec; } bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const { return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() )); } bool XMLDeclaration::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLUnknown ---------- // XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) { } XMLUnknown::~XMLUnknown() { } char* XMLUnknown::ParseDeep( char* p, StrPair* ) { // Unknown parses as text. const char* start = p; p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); if ( !p ) { _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); } return p; } XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? return text; } bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const { return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() )); } bool XMLUnknown::Accept( XMLVisitor* visitor ) const { return visitor->Visit( *this ); } // --------- XMLAttribute ---------- // char* XMLAttribute::ParseDeep( char* p, bool processEntities ) { // Parse using the name rules: bug fix, was using ParseText before p = _name.ParseName( p ); if ( !p || !*p ) { return 0; } // Skip white space before = p = XMLUtil::SkipWhiteSpace( p ); if ( !p || *p != '=' ) { return 0; } ++p; // move up to opening quote p = XMLUtil::SkipWhiteSpace( p ); if ( *p != '\"' && *p != '\'' ) { return 0; } char endTag[2] = { *p, 0 }; ++p; // move past opening quote p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); return p; } void XMLAttribute::SetName( const char* n ) { _name.SetStr( n ); } XMLError XMLAttribute::QueryIntValue( int* value ) const { if ( XMLUtil::ToInt( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const { if ( XMLUtil::ToUnsigned( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryBoolValue( bool* value ) const { if ( XMLUtil::ToBool( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryFloatValue( float* value ) const { if ( XMLUtil::ToFloat( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } XMLError XMLAttribute::QueryDoubleValue( double* value ) const { if ( XMLUtil::ToDouble( Value(), value )) { return XML_NO_ERROR; } return XML_WRONG_ATTRIBUTE_TYPE; } void XMLAttribute::SetAttribute( const char* v ) { _value.SetStr( v ); } void XMLAttribute::SetAttribute( int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } void XMLAttribute::SetAttribute( float v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); _value.SetStr( buf ); } // --------- XMLElement ---------- // XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), _closingType( 0 ), _rootAttribute( 0 ) { } XMLElement::~XMLElement() { while( _rootAttribute ) { XMLAttribute* next = _rootAttribute->_next; DELETE_ATTRIBUTE( _rootAttribute ); _rootAttribute = next; } } XMLAttribute* XMLElement::FindAttribute( const char* name ) { XMLAttribute* a = 0; for( a=_rootAttribute; a; a = a->_next ) { if ( XMLUtil::StringEqual( a->Name(), name ) ) { return a; } } return 0; } const XMLAttribute* XMLElement::FindAttribute( const char* name ) const { XMLAttribute* a = 0; for( a=_rootAttribute; a; a = a->_next ) { if ( XMLUtil::StringEqual( a->Name(), name ) ) { return a; } } return 0; } const char* XMLElement::Attribute( const char* name, const char* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return 0; } if ( !value || XMLUtil::StringEqual( a->Value(), value )) { return a->Value(); } return 0; } const char* XMLElement::GetText() const { if ( FirstChild() && FirstChild()->ToText() ) { return FirstChild()->ToText()->Value(); } return 0; } XMLError XMLElement::QueryIntText( int* ival ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->ToText()->Value(); if ( XMLUtil::ToInt( t, ival ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->ToText()->Value(); if ( XMLUtil::ToUnsigned( t, uval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryBoolText( bool* bval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->ToText()->Value(); if ( XMLUtil::ToBool( t, bval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryDoubleText( double* dval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->ToText()->Value(); if ( XMLUtil::ToDouble( t, dval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLError XMLElement::QueryFloatText( float* fval ) const { if ( FirstChild() && FirstChild()->ToText() ) { const char* t = FirstChild()->ToText()->Value(); if ( XMLUtil::ToFloat( t, fval ) ) { return XML_SUCCESS; } return XML_CAN_NOT_CONVERT_TEXT; } return XML_NO_TEXT_NODE; } XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) { XMLAttribute* last = 0; XMLAttribute* attrib = 0; for( attrib = _rootAttribute; attrib; last = attrib, attrib = attrib->_next ) { if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { break; } } if ( !attrib ) { attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); attrib->_memPool = &_document->_attributePool; if ( last ) { last->_next = attrib; } else { _rootAttribute = attrib; } attrib->SetName( name ); attrib->_memPool->SetTracked(); // always created and linked. } return attrib; } void XMLElement::DeleteAttribute( const char* name ) { XMLAttribute* prev = 0; for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { if ( XMLUtil::StringEqual( name, a->Name() ) ) { if ( prev ) { prev->_next = a->_next; } else { _rootAttribute = a->_next; } DELETE_ATTRIBUTE( a ); break; } prev = a; } } char* XMLElement::ParseAttributes( char* p ) { const char* start = p; XMLAttribute* prevAttribute = 0; // Read the attributes. while( p ) { p = XMLUtil::SkipWhiteSpace( p ); if ( !p || !(*p) ) { _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); return 0; } // attribute. if ( XMLUtil::IsAlpha( *p ) ) { XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); attrib->_memPool = &_document->_attributePool; attrib->_memPool->SetTracked(); p = attrib->ParseDeep( p, _document->ProcessEntities() ); if ( !p || Attribute( attrib->Name() ) ) { DELETE_ATTRIBUTE( attrib ); _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); return 0; } // There is a minor bug here: if the attribute in the source xml // document is duplicated, it will not be detected and the // attribute will be doubly added. However, tracking the 'prevAttribute' // avoids re-scanning the attribute list. Preferring performance for // now, may reconsider in the future. if ( prevAttribute ) { prevAttribute->_next = attrib; } else { _rootAttribute = attrib; } prevAttribute = attrib; } // end of the tag else if ( *p == '/' && *(p+1) == '>' ) { _closingType = CLOSED; return p+2; // done; sealed element. } // end of the tag else if ( *p == '>' ) { ++p; break; } else { _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); return 0; } } return p; } // // // foobar // char* XMLElement::ParseDeep( char* p, StrPair* strPair ) { // Read the element name. p = XMLUtil::SkipWhiteSpace( p ); if ( !p ) { return 0; } // The closing element is the form. It is // parsed just like a regular element then deleted from // the DOM. if ( *p == '/' ) { _closingType = CLOSING; ++p; } p = _value.ParseName( p ); if ( _value.Empty() ) { return 0; } p = ParseAttributes( p ); if ( !p || !*p || _closingType ) { return p; } p = XMLNode::ParseDeep( p, strPair ); return p; } XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const { if ( !doc ) { doc = _document; } XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? } return element; } bool XMLElement::ShallowEqual( const XMLNode* compare ) const { const XMLElement* other = compare->ToElement(); if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { const XMLAttribute* a=FirstAttribute(); const XMLAttribute* b=other->FirstAttribute(); while ( a && b ) { if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { return false; } a = a->Next(); b = b->Next(); } if ( a || b ) { // different count return false; } return true; } return false; } bool XMLElement::Accept( XMLVisitor* visitor ) const { if ( visitor->VisitEnter( *this, _rootAttribute ) ) { for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { if ( !node->Accept( visitor ) ) { break; } } } return visitor->VisitExit( *this ); } // --------- XMLDocument ----------- // XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : XMLNode( 0 ), _writeBOM( false ), _processEntities( processEntities ), _errorID( XML_NO_ERROR ), _whitespace( whitespace ), _errorStr1( 0 ), _errorStr2( 0 ), _charBuffer( 0 ) { _document = this; // avoid warning about 'this' in initializer list } XMLDocument::~XMLDocument() { DeleteChildren(); delete [] _charBuffer; #if 0 textPool.Trace( "text" ); elementPool.Trace( "element" ); commentPool.Trace( "comment" ); attributePool.Trace( "attribute" ); #endif #ifdef DEBUG if ( Error() == false ) { TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); } #endif } void XMLDocument::InitDocument() { _errorID = XML_NO_ERROR; _errorStr1 = 0; _errorStr2 = 0; delete [] _charBuffer; _charBuffer = 0; } XMLElement* XMLDocument::NewElement( const char* name ) { XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); ele->_memPool = &_elementPool; ele->SetName( name ); return ele; } XMLComment* XMLDocument::NewComment( const char* str ) { XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); comment->_memPool = &_commentPool; comment->SetValue( str ); return comment; } XMLText* XMLDocument::NewText( const char* str ) { XMLText* text = new (_textPool.Alloc()) XMLText( this ); text->_memPool = &_textPool; text->SetValue( str ); return text; } XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) { XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); dec->_memPool = &_commentPool; dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); return dec; } XMLUnknown* XMLDocument::NewUnknown( const char* str ) { XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); unk->_memPool = &_commentPool; unk->SetValue( str ); return unk; } XMLError XMLDocument::LoadFile( const char* filename ) { DeleteChildren(); InitDocument(); FILE* fp = 0; #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) errno_t err = fopen_s(&fp, filename, "rb" ); if ( !fp || err) { #else fp = fopen( filename, "rb" ); if ( !fp) { #endif SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); return _errorID; } LoadFile( fp ); fclose( fp ); return _errorID; } XMLError XMLDocument::LoadFile( FILE* fp ) { DeleteChildren(); InitDocument(); fseek( fp, 0, SEEK_END ); size_t size = ftell( fp ); fseek( fp, 0, SEEK_SET ); if ( size == 0 ) { return _errorID; } _charBuffer = new char[size+1]; size_t read = fread( _charBuffer, 1, size, fp ); if ( read != size ) { SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); return _errorID; } _charBuffer[size] = 0; const char* p = _charBuffer; p = XMLUtil::SkipWhiteSpace( p ); p = XMLUtil::ReadBOM( p, &_writeBOM ); if ( !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } ParseDeep( _charBuffer + (p-_charBuffer), 0 ); return _errorID; } XMLError XMLDocument::SaveFile( const char* filename, bool compact ) { FILE* fp = 0; #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) errno_t err = fopen_s(&fp, filename, "w" ); if ( !fp || err) { #else fp = fopen( filename, "w" ); if ( !fp) { #endif SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); return _errorID; } SaveFile(fp, compact); fclose( fp ); return _errorID; } XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) { XMLPrinter stream( fp, compact ); Print( &stream ); return _errorID; } XMLError XMLDocument::Parse( const char* p, size_t len ) { DeleteChildren(); InitDocument(); if ( !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } if ( len == (size_t)(-1) ) { len = strlen( p ); } _charBuffer = new char[ len+1 ]; memcpy( _charBuffer, p, len ); _charBuffer[len] = 0; p = XMLUtil::SkipWhiteSpace( p ); p = XMLUtil::ReadBOM( p, &_writeBOM ); if ( !p || !*p ) { SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); return _errorID; } ParseDeep( _charBuffer, 0 ); return _errorID; } void XMLDocument::Print( XMLPrinter* streamer ) { XMLPrinter stdStreamer( stdout ); if ( !streamer ) { streamer = &stdStreamer; } Accept( streamer ); } void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) { _errorID = error; _errorStr1 = str1; _errorStr2 = str2; } void XMLDocument::PrintError() const { if ( _errorID ) { static const int LEN = 20; char buf1[LEN] = { 0 }; char buf2[LEN] = { 0 }; if ( _errorStr1 ) { TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); } if ( _errorStr2 ) { TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); } printf( "XMLDocument error id=%d str1=%s str2=%s\n", _errorID, buf1, buf2 ); } } XMLPrinter::XMLPrinter( FILE* file, bool compact ) : _elementJustOpened( false ), _firstElement( true ), _fp( file ), _depth( 0 ), _textDepth( -1 ), _processEntities( true ), _compactMode( compact ) { for( int i=0; i'] = true; // not required, but consistency is nice _buffer.Push( 0 ); } void XMLPrinter::Print( const char* format, ... ) { va_list va; va_start( va, format ); if ( _fp ) { vfprintf( _fp, format, va ); } else { // This seems brutally complex. Haven't figured out a better // way on windows. #ifdef _MSC_VER int len = -1; int expand = 1000; while ( len < 0 ) { len = vsnprintf_s( _accumulator.Mem(), _accumulator.Capacity(), _TRUNCATE, format, va ); if ( len < 0 ) { expand *= 3/2; _accumulator.PushArr( expand ); } } char* p = _buffer.PushArr( len ) - 1; memcpy( p, _accumulator.Mem(), len+1 ); #else int len = vsnprintf( 0, 0, format, va ); // Close out and re-start the va-args va_end( va ); va_start( va, format ); char* p = _buffer.PushArr( len ) - 1; vsnprintf( p, len+1, format, va ); #endif } va_end( va ); } void XMLPrinter::PrintSpace( int depth ) { for( int i=0; i 0 && *q < ENTITY_RANGE ) { // Check for entities. If one is found, flush // the stream up until the entity, write the // entity, and keep looking. if ( flag[(unsigned)(*q)] ) { while ( p < q ) { Print( "%c", *p ); ++p; } for( int i=0; i 0) ) { Print( "%s", p ); } } void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) { static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; if ( writeBOM ) { Print( "%s", bom ); } if ( writeDec ) { PushDeclaration( "xml version=\"1.0\"" ); } } void XMLPrinter::OpenElement( const char* name ) { if ( _elementJustOpened ) { SealElement(); } _stack.Push( name ); if ( _textDepth < 0 && !_firstElement && !_compactMode ) { Print( "\n" ); PrintSpace( _depth ); } Print( "<%s", name ); _elementJustOpened = true; _firstElement = false; ++_depth; } void XMLPrinter::PushAttribute( const char* name, const char* value ) { TIXMLASSERT( _elementJustOpened ); Print( " %s=\"", name ); PrintString( value, false ); Print( "\"" ); } void XMLPrinter::PushAttribute( const char* name, int v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, unsigned v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, bool v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::PushAttribute( const char* name, double v ) { char buf[BUF_SIZE]; XMLUtil::ToStr( v, buf, BUF_SIZE ); PushAttribute( name, buf ); } void XMLPrinter::CloseElement() { --_depth; const char* name = _stack.Pop(); if ( _elementJustOpened ) { Print( "/>" ); } else { if ( _textDepth < 0 && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } Print( "", name ); } if ( _textDepth == _depth ) { _textDepth = -1; } if ( _depth == 0 && !_compactMode) { Print( "\n" ); } _elementJustOpened = false; } void XMLPrinter::SealElement() { _elementJustOpened = false; Print( ">" ); } void XMLPrinter::PushText( const char* text, bool cdata ) { _textDepth = _depth-1; if ( _elementJustOpened ) { SealElement(); } if ( cdata ) { Print( "" ); } else { PrintString( text, true ); } } void XMLPrinter::PushText( int value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( unsigned value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( bool value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( float value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushText( double value ) { char buf[BUF_SIZE]; XMLUtil::ToStr( value, buf, BUF_SIZE ); PushText( buf, false ); } void XMLPrinter::PushComment( const char* comment ) { if ( _elementJustOpened ) { SealElement(); } if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } _firstElement = false; Print( "", comment ); } void XMLPrinter::PushDeclaration( const char* value ) { if ( _elementJustOpened ) { SealElement(); } if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } _firstElement = false; Print( "", value ); } void XMLPrinter::PushUnknown( const char* value ) { if ( _elementJustOpened ) { SealElement(); } if ( _textDepth < 0 && !_firstElement && !_compactMode) { Print( "\n" ); PrintSpace( _depth ); } _firstElement = false; Print( "", value ); } bool XMLPrinter::VisitEnter( const XMLDocument& doc ) { _processEntities = doc.ProcessEntities(); if ( doc.HasBOM() ) { PushHeader( true, false ); } return true; } bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) { OpenElement( element.Name() ); while ( attribute ) { PushAttribute( attribute->Name(), attribute->Value() ); attribute = attribute->Next(); } return true; } bool XMLPrinter::VisitExit( const XMLElement& ) { CloseElement(); return true; } bool XMLPrinter::Visit( const XMLText& text ) { PushText( text.Value(), text.CData() ); return true; } bool XMLPrinter::Visit( const XMLComment& comment ) { PushComment( comment.Value() ); return true; } bool XMLPrinter::Visit( const XMLDeclaration& declaration ) { PushDeclaration( declaration.Value() ); return true; } bool XMLPrinter::Visit( const XMLUnknown& unknown ) { PushUnknown( unknown.Value() ); return true; } } // namespace tinyxml2 \ No newline at end of file diff --git a/modules/oculus_sdk_mac/3rdParty/TinyXml/tinyxml2.h b/modules/oculus_sdk_mac/3rdParty/TinyXml/tinyxml2.h new file mode 100644 index 0000000..346f439 --- /dev/null +++ b/modules/oculus_sdk_mac/3rdParty/TinyXml/tinyxml2.h @@ -0,0 +1 @@ +/* Original code by Lee Thomason (www.grinninglizard.com) This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYXML2_INCLUDED #define TINYXML2_INCLUDED #if defined(ANDROID_NDK) || defined(__BORLANDC__) # include # include # include # include # include # include #else # include # include # include # include # include # include #endif /* TODO: intern strings instead of allocation. */ /* gcc: g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe Formatting, Artistic Style: AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h */ #if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) # ifndef DEBUG # define DEBUG # endif #endif #if defined(DEBUG) # if defined(_MSC_VER) # define TIXMLASSERT( x ) if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak() # elif defined (ANDROID_NDK) # include # define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } # else # include # define TIXMLASSERT assert # endif # else # define TIXMLASSERT( x ) {} #endif #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) // Microsoft visual studio, version 2005 and higher. /*int _snprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format [, argument] ... );*/ inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) { va_list va; va_start( va, format ); int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); va_end( va ); return result; } #define TIXML_SSCANF sscanf_s #else // GCC version 3 and higher //#warning( "Using sn* functions." ) #define TIXML_SNPRINTF snprintf #define TIXML_SSCANF sscanf #endif static const int TIXML2_MAJOR_VERSION = 1; static const int TIXML2_MINOR_VERSION = 0; static const int TIXML2_PATCH_VERSION = 9; namespace tinyxml2 { class XMLDocument; class XMLElement; class XMLAttribute; class XMLComment; class XMLNode; class XMLText; class XMLDeclaration; class XMLUnknown; class XMLPrinter; /* A class that wraps strings. Normally stores the start and end pointers into the XML file itself, and will apply normalization and entity translation if actually read. Can also store (and memory manage) a traditional char[] */ class StrPair { public: enum { NEEDS_ENTITY_PROCESSING = 0x01, NEEDS_NEWLINE_NORMALIZATION = 0x02, COLLAPSE_WHITESPACE = 0x04, TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_NAME = 0, ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, COMMENT = NEEDS_NEWLINE_NORMALIZATION }; StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} ~StrPair(); void Set( char* start, char* end, int flags ) { Reset(); _start = start; _end = end; _flags = flags | NEEDS_FLUSH; } const char* GetStr(); bool Empty() const { return _start == _end; } void SetInternedStr( const char* str ) { Reset(); _start = const_cast(str); } void SetStr( const char* str, int flags=0 ); char* ParseText( char* in, const char* endTag, int strFlags ); char* ParseName( char* in ); private: void Reset(); void CollapseWhitespace(); enum { NEEDS_FLUSH = 0x100, NEEDS_DELETE = 0x200 }; // After parsing, if *end != 0, it can be set to zero. int _flags; char* _start; char* _end; }; /* A dynamic array of Plain Old Data. Doesn't support constructors, etc. Has a small initial memory pool, so that low or no usage will not cause a call to new/delete */ template class DynArray { public: DynArray< T, INIT >() { _mem = _pool; _allocated = INIT; _size = 0; } ~DynArray() { if ( _mem != _pool ) { delete [] _mem; } } void Push( T t ) { EnsureCapacity( _size+1 ); _mem[_size++] = t; } T* PushArr( int count ) { EnsureCapacity( _size+count ); T* ret = &_mem[_size]; _size += count; return ret; } T Pop() { return _mem[--_size]; } void PopArr( int count ) { TIXMLASSERT( _size >= count ); _size -= count; } bool Empty() const { return _size == 0; } T& operator[](int i) { TIXMLASSERT( i>= 0 && i < _size ); return _mem[i]; } const T& operator[](int i) const { TIXMLASSERT( i>= 0 && i < _size ); return _mem[i]; } int Size() const { return _size; } int Capacity() const { return _allocated; } const T* Mem() const { return _mem; } T* Mem() { return _mem; } private: void EnsureCapacity( int cap ) { if ( cap > _allocated ) { int newAllocated = cap * 2; T* newMem = new T[newAllocated]; memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs if ( _mem != _pool ) { delete [] _mem; } _mem = newMem; _allocated = newAllocated; } } T* _mem; T _pool[INIT]; int _allocated; // objects allocated int _size; // number objects in use }; /* Parent virtual class of a pool for fast allocation and deallocation of objects. */ class MemPool { public: MemPool() {} virtual ~MemPool() {} virtual int ItemSize() const = 0; virtual void* Alloc() = 0; virtual void Free( void* ) = 0; virtual void SetTracked() = 0; }; /* Template child class to create pools of the correct type. */ template< int SIZE > class MemPoolT : public MemPool { public: MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} ~MemPoolT() { // Delete the blocks. for( int i=0; i<_blockPtrs.Size(); ++i ) { delete _blockPtrs[i]; } } virtual int ItemSize() const { return SIZE; } int CurrentAllocs() const { return _currentAllocs; } virtual void* Alloc() { if ( !_root ) { // Need a new block. Block* block = new Block(); _blockPtrs.Push( block ); for( int i=0; ichunk[i].next = &block->chunk[i+1]; } block->chunk[COUNT-1].next = 0; _root = block->chunk; } void* result = _root; _root = _root->next; ++_currentAllocs; if ( _currentAllocs > _maxAllocs ) { _maxAllocs = _currentAllocs; } _nAllocs++; _nUntracked++; return result; } virtual void Free( void* mem ) { if ( !mem ) { return; } --_currentAllocs; Chunk* chunk = (Chunk*)mem; #ifdef DEBUG memset( chunk, 0xfe, sizeof(Chunk) ); #endif chunk->next = _root; _root = chunk; } void Trace( const char* name ) { printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); } void SetTracked() { _nUntracked--; } int Untracked() const { return _nUntracked; } enum { COUNT = 1024/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private private: union Chunk { Chunk* next; char mem[SIZE]; }; struct Block { Chunk chunk[COUNT]; }; DynArray< Block*, 10 > _blockPtrs; Chunk* _root; int _currentAllocs; int _nAllocs; int _maxAllocs; int _nUntracked; }; /** Implements the interface to the "Visitor pattern" (see the Accept() method.) If you call the Accept() method, it requires being passed a XMLVisitor class to handle callbacks. For nodes that contain other nodes (Document, Element) you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs are simply called with Visit(). If you return 'true' from a Visit method, recursive parsing will continue. If you return false, no children of this node or its sibilings will be visited. All flavors of Visit methods have a default implementation that returns 'true' (continue visiting). You need to only override methods that are interesting to you. Generally Accept() is called on the TiXmlDocument, although all nodes support visiting. You should never change the document from a callback. @sa XMLNode::Accept() */ class XMLVisitor { public: virtual ~XMLVisitor() {} /// Visit a document. virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { return true; } /// Visit a document. virtual bool VisitExit( const XMLDocument& /*doc*/ ) { return true; } /// Visit an element. virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { return true; } /// Visit an element. virtual bool VisitExit( const XMLElement& /*element*/ ) { return true; } /// Visit a declaration. virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { return true; } /// Visit a text node. virtual bool Visit( const XMLText& /*text*/ ) { return true; } /// Visit a comment node. virtual bool Visit( const XMLComment& /*comment*/ ) { return true; } /// Visit an unknown node. virtual bool Visit( const XMLUnknown& /*unknown*/ ) { return true; } }; /* Utility functionality. */ class XMLUtil { public: // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't // correct, but simple, and usually works. static const char* SkipWhiteSpace( const char* p ) { while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { ++p; } return p; } static char* SkipWhiteSpace( char* p ) { while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast(p) ) ) { ++p; } return p; } static bool IsWhiteSpace( char p ) { return !IsUTF8Continuation(p) && isspace( static_cast(p) ); } inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { int n = 0; if ( p == q ) { return true; } while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); } /// Get the last child node, or null if none exists. const XMLNode* LastChild() const { return _lastChild; } XMLNode* LastChild() { return const_cast(const_cast(this)->LastChild() ); } /** Get the last child element or optionally the last child element with the specified name. */ const XMLElement* LastChildElement( const char* value=0 ) const; XMLElement* LastChildElement( const char* value=0 ) { return const_cast(const_cast(this)->LastChildElement(value) ); } /// Get the previous (left) sibling node of this node. const XMLNode* PreviousSibling() const { return _prev; } XMLNode* PreviousSibling() { return _prev; } /// Get the previous (left) sibling element of this node, with an opitionally supplied name. const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; XMLElement* PreviousSiblingElement( const char* value=0 ) { return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); } /// Get the next (right) sibling node of this node. const XMLNode* NextSibling() const { return _next; } XMLNode* NextSibling() { return _next; } /// Get the next (right) sibling element of this node, with an opitionally supplied name. const XMLElement* NextSiblingElement( const char* value=0 ) const; XMLElement* NextSiblingElement( const char* value=0 ) { return const_cast(const_cast(this)->NextSiblingElement( value ) ); } /** Add a child node as the last (right) child. */ XMLNode* InsertEndChild( XMLNode* addThis ); XMLNode* LinkEndChild( XMLNode* addThis ) { return InsertEndChild( addThis ); } /** Add a child node as the first (left) child. */ XMLNode* InsertFirstChild( XMLNode* addThis ); /** Add a node after the specified child node. */ XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); /** Delete all the children of this node. */ void DeleteChildren(); /** Delete a child of this node. */ void DeleteChild( XMLNode* node ); /** Make a copy of this node, but not its children. You may pass in a Document pointer that will be the owner of the new Node. If the 'document' is null, then the node returned will be allocated from the current Document. (this->GetDocument()) Note: if called on a XMLDocument, this will return null. */ virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; /** Test if 2 nodes are the same, but don't test children. The 2 nodes do not need to be in the same Document. Note: if called on a XMLDocument, this will return false. */ virtual bool ShallowEqual( const XMLNode* compare ) const = 0; /** Accept a hierarchical visit of the nodes in the TinyXML DOM. Every node in the XML tree will be conditionally visited and the host will be called back via the TiXmlVisitor interface. This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse the XML for the callbacks, so the performance of TinyXML is unchanged by using this interface versus any other.) The interface has been based on ideas from: - http://www.saxproject.org/ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern Which are both good references for "visiting". An example of using Accept(): @verbatim TiXmlPrinter printer; tinyxmlDoc.Accept( &printer ); const char* xmlcstr = printer.CStr(); @endverbatim */ virtual bool Accept( XMLVisitor* visitor ) const = 0; // internal virtual char* ParseDeep( char*, StrPair* ); protected: XMLNode( XMLDocument* ); virtual ~XMLNode(); XMLNode( const XMLNode& ); // not supported XMLNode& operator=( const XMLNode& ); // not supported XMLDocument* _document; XMLNode* _parent; mutable StrPair _value; XMLNode* _firstChild; XMLNode* _lastChild; XMLNode* _prev; XMLNode* _next; private: MemPool* _memPool; void Unlink( XMLNode* child ); }; /** XML text. Note that a text node can have child element nodes, for example: @verbatim This is bold @endverbatim A text node can have 2 ways to output the next. "normal" output and CDATA. It will default to the mode it was parsed from the XML file and you generally want to leave it alone, but you can change the output mode with SetCDATA() and query it with CDATA(). */ class XMLText : public XMLNode { friend class XMLBase; friend class XMLDocument; public: virtual bool Accept( XMLVisitor* visitor ) const; virtual XMLText* ToText() { return this; } virtual const XMLText* ToText() const { return this; } /// Declare whether this should be CDATA or standard text. void SetCData( bool isCData ) { _isCData = isCData; } /// Returns true if this is a CDATA text element. bool CData() const { return _isCData; } char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} virtual ~XMLText() {} XMLText( const XMLText& ); // not supported XMLText& operator=( const XMLText& ); // not supported private: bool _isCData; }; /** An XML Comment. */ class XMLComment : public XMLNode { friend class XMLDocument; public: virtual XMLComment* ToComment() { return this; } virtual const XMLComment* ToComment() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLComment( XMLDocument* doc ); virtual ~XMLComment(); XMLComment( const XMLComment& ); // not supported XMLComment& operator=( const XMLComment& ); // not supported private: }; /** In correct XML the declaration is the first entry in the file. @verbatim @endverbatim TinyXML2 will happily read or write files without a declaration, however. The text of the declaration isn't interpreted. It is parsed and written as a string. */ class XMLDeclaration : public XMLNode { friend class XMLDocument; public: virtual XMLDeclaration* ToDeclaration() { return this; } virtual const XMLDeclaration* ToDeclaration() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLDeclaration( XMLDocument* doc ); virtual ~XMLDeclaration(); XMLDeclaration( const XMLDeclaration& ); // not supported XMLDeclaration& operator=( const XMLDeclaration& ); // not supported }; /** Any tag that tinyXml doesn't recognize is saved as an unknown. It is a tag of text, but should not be modified. It will be written back to the XML, unchanged, when the file is saved. DTD tags get thrown into TiXmlUnknowns. */ class XMLUnknown : public XMLNode { friend class XMLDocument; public: virtual XMLUnknown* ToUnknown() { return this; } virtual const XMLUnknown* ToUnknown() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; char* ParseDeep( char*, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; protected: XMLUnknown( XMLDocument* doc ); virtual ~XMLUnknown(); XMLUnknown( const XMLUnknown& ); // not supported XMLUnknown& operator=( const XMLUnknown& ); // not supported }; enum XMLError { XML_NO_ERROR = 0, XML_SUCCESS = 0, XML_NO_ATTRIBUTE, XML_WRONG_ATTRIBUTE_TYPE, XML_ERROR_FILE_NOT_FOUND, XML_ERROR_FILE_COULD_NOT_BE_OPENED, XML_ERROR_FILE_READ_ERROR, XML_ERROR_ELEMENT_MISMATCH, XML_ERROR_PARSING_ELEMENT, XML_ERROR_PARSING_ATTRIBUTE, XML_ERROR_IDENTIFYING_TAG, XML_ERROR_PARSING_TEXT, XML_ERROR_PARSING_CDATA, XML_ERROR_PARSING_COMMENT, XML_ERROR_PARSING_DECLARATION, XML_ERROR_PARSING_UNKNOWN, XML_ERROR_EMPTY_DOCUMENT, XML_ERROR_MISMATCHED_ELEMENT, XML_ERROR_PARSING, XML_CAN_NOT_CONVERT_TEXT, XML_NO_TEXT_NODE }; /** An attribute is a name-value pair. Elements have an arbitrary number of attributes, each with a unique name. @note The attributes are not XMLNodes. You may only query the Next() attribute in a list. */ class XMLAttribute { friend class XMLElement; public: /// The name of the attribute. const char* Name() const { return _name.GetStr(); } /// The value of the attribute. const char* Value() const { return _value.GetStr(); } /// The next attribute in the list. const XMLAttribute* Next() const { return _next; } /** IntAttribute interprets the attribute as an integer, and returns the value. If the value isn't an integer, 0 will be returned. There is no error checking; use QueryIntAttribute() if you need error checking. */ int IntValue() const { int i=0; QueryIntValue( &i ); return i; } /// Query as an unsigned integer. See IntAttribute() unsigned UnsignedValue() const { unsigned i=0; QueryUnsignedValue( &i ); return i; } /// Query as a boolean. See IntAttribute() bool BoolValue() const { bool b=false; QueryBoolValue( &b ); return b; } /// Query as a double. See IntAttribute() double DoubleValue() const { double d=0; QueryDoubleValue( &d ); return d; } /// Query as a float. See IntAttribute() float FloatValue() const { float f=0; QueryFloatValue( &f ); return f; } /** QueryIntAttribute interprets the attribute as an integer, and returns the value in the provided paremeter. The function will return XML_NO_ERROR on success, and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. */ XMLError QueryIntValue( int* value ) const; /// See QueryIntAttribute XMLError QueryUnsignedValue( unsigned int* value ) const; /// See QueryIntAttribute XMLError QueryBoolValue( bool* value ) const; /// See QueryIntAttribute XMLError QueryDoubleValue( double* value ) const; /// See QueryIntAttribute XMLError QueryFloatValue( float* value ) const; /// Set the attribute to a string value. void SetAttribute( const char* value ); /// Set the attribute to value. void SetAttribute( int value ); /// Set the attribute to value. void SetAttribute( unsigned value ); /// Set the attribute to value. void SetAttribute( bool value ); /// Set the attribute to value. void SetAttribute( double value ); /// Set the attribute to value. void SetAttribute( float value ); private: enum { BUF_SIZE = 200 }; XMLAttribute() : _next( 0 ) {} virtual ~XMLAttribute() {} XMLAttribute( const XMLAttribute& ); // not supported void operator=( const XMLAttribute& ); // not supported void SetName( const char* name ); char* ParseDeep( char* p, bool processEntities ); mutable StrPair _name; mutable StrPair _value; XMLAttribute* _next; MemPool* _memPool; }; /** The element is a container class. It has a value, the element name, and can contain other elements, text, comments, and unknowns. Elements also contain an arbitrary number of attributes. */ class XMLElement : public XMLNode { friend class XMLBase; friend class XMLDocument; public: /// Get the name of an element (which is the Value() of the node.) const char* Name() const { return Value(); } /// Set the name of the element. void SetName( const char* str, bool staticMem=false ) { SetValue( str, staticMem ); } virtual XMLElement* ToElement() { return this; } virtual const XMLElement* ToElement() const { return this; } virtual bool Accept( XMLVisitor* visitor ) const; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. For example: @verbatim const char* value = ele->Attribute( "foo" ); @endverbatim The 'value' parameter is normally null. However, if specified, the attribute will only be returned if the 'name' and 'value' match. This allow you to write code: @verbatim if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); @endverbatim rather than: @verbatim if ( ele->Attribute( "foo" ) ) { if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); } @endverbatim */ const char* Attribute( const char* name, const char* value=0 ) const; /** Given an attribute name, IntAttribute() returns the value of the attribute interpreted as an integer. 0 will be returned if there is an error. For a method with error checking, see QueryIntAttribute() */ int IntAttribute( const char* name ) const { int i=0; QueryIntAttribute( name, &i ); return i; } /// See IntAttribute() unsigned UnsignedAttribute( const char* name ) const { unsigned i=0; QueryUnsignedAttribute( name, &i ); return i; } /// See IntAttribute() bool BoolAttribute( const char* name ) const { bool b=false; QueryBoolAttribute( name, &b ); return b; } /// See IntAttribute() double DoubleAttribute( const char* name ) const { double d=0; QueryDoubleAttribute( name, &d ); return d; } /// See IntAttribute() float FloatAttribute( const char* name ) const { float f=0; QueryFloatAttribute( name, &f ); return f; } /** Given an attribute name, QueryIntAttribute() returns XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion can't be performed, or XML_NO_ATTRIBUTE if the attribute doesn't exist. If successful, the result of the conversion will be written to 'value'. If not successful, nothing will be written to 'value'. This allows you to provide default value: @verbatim int value = 10; QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 @endverbatim */ XMLError QueryIntAttribute( const char* name, int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryIntValue( value ); } /// See QueryIntAttribute() XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryUnsignedValue( value ); } /// See QueryIntAttribute() XMLError QueryBoolAttribute( const char* name, bool* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryBoolValue( value ); } /// See QueryIntAttribute() XMLError QueryDoubleAttribute( const char* name, double* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryDoubleValue( value ); } /// See QueryIntAttribute() XMLError QueryFloatAttribute( const char* name, float* value ) const { const XMLAttribute* a = FindAttribute( name ); if ( !a ) { return XML_NO_ATTRIBUTE; } return a->QueryFloatValue( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, const char* value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, int value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, unsigned value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, bool value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /// Sets the named attribute to value. void SetAttribute( const char* name, double value ) { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( value ); } /** Delete an attribute. */ void DeleteAttribute( const char* name ); /// Return the first attribute in the list. const XMLAttribute* FirstAttribute() const { return _rootAttribute; } /// Query a specific attribute in the list. const XMLAttribute* FindAttribute( const char* name ) const; /** Convenience function for easy access to the text inside an element. Although easy and concise, GetText() is limited compared to getting the TiXmlText child and accessing it directly. If the first child of 'this' is a TiXmlText, the GetText() returns the character string of the Text node, else null is returned. This is a convenient method for getting the text of simple contained text: @verbatim This is text const char* str = fooElement->GetText(); @endverbatim 'str' will be a pointer to "This is text". Note that this function can be misleading. If the element foo was created from this XML: @verbatim This is text @endverbatim then the value of str would be null. The first child node isn't a text node, it is another element. From this XML: @verbatim This is text @endverbatim GetText() will return "This is ". */ const char* GetText() const; /** Convenience method to query the value of a child text node. This is probably best shown by example. Given you have a document is this form: @verbatim 1 1.4 @endverbatim The QueryIntText() and similar functions provide a safe and easier way to get to the "value" of x and y. @verbatim int x = 0; float y = 0; // types of x and y are contrived for example const XMLElement* xElement = pointElement->FirstChildElement( "x" ); const XMLElement* yElement = pointElement->FirstChildElement( "y" ); xElement->QueryIntText( &x ); yElement->QueryFloatText( &y ); @endverbatim @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. */ XMLError QueryIntText( int* ival ) const; /// See QueryIntText() XMLError QueryUnsignedText( unsigned* uval ) const; /// See QueryIntText() XMLError QueryBoolText( bool* bval ) const; /// See QueryIntText() XMLError QueryDoubleText( double* dval ) const; /// See QueryIntText() XMLError QueryFloatText( float* fval ) const; // internal: enum { OPEN, // CLOSED, // CLOSING // }; int ClosingType() const { return _closingType; } char* ParseDeep( char* p, StrPair* endTag ); virtual XMLNode* ShallowClone( XMLDocument* document ) const; virtual bool ShallowEqual( const XMLNode* compare ) const; private: XMLElement( XMLDocument* doc ); virtual ~XMLElement(); XMLElement( const XMLElement& ); // not supported void operator=( const XMLElement& ); // not supported XMLAttribute* FindAttribute( const char* name ); XMLAttribute* FindOrCreateAttribute( const char* name ); //void LinkAttribute( XMLAttribute* attrib ); char* ParseAttributes( char* p ); int _closingType; // The attribute list is ordered; there is no 'lastAttribute' // because the list needs to be scanned for dupes before adding // a new attribute. XMLAttribute* _rootAttribute; }; enum Whitespace { PRESERVE_WHITESPACE, COLLAPSE_WHITESPACE }; /** A Document binds together all the functionality. It can be saved, loaded, and printed to the screen. All Nodes are connected and allocated to a Document. If the Document is deleted, all its Nodes are also deleted. */ class XMLDocument : public XMLNode { friend class XMLElement; public: /// constructor XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); ~XMLDocument(); virtual XMLDocument* ToDocument() { return this; } virtual const XMLDocument* ToDocument() const { return this; } /** Parse an XML file from a character string. Returns XML_NO_ERROR (0) on success, or an errorID. You may optionally pass in the 'nBytes', which is the number of bytes which will be parsed. If not specified, TinyXML will assume 'xml' points to a null terminated string. */ XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); /** Load an XML file from disk. Returns XML_NO_ERROR (0) on success, or an errorID. */ XMLError LoadFile( const char* filename ); /** Load an XML file from disk. You are responsible for providing and closing the FILE*. Returns XML_NO_ERROR (0) on success, or an errorID. */ XMLError LoadFile( FILE* ); /** Save the XML file to disk. Returns XML_NO_ERROR (0) on success, or an errorID. */ XMLError SaveFile( const char* filename, bool compact = false ); /** Save the XML file to disk. You are responsible for providing and closing the FILE*. Returns XML_NO_ERROR (0) on success, or an errorID. */ XMLError SaveFile( FILE* fp, bool compact = false ); bool ProcessEntities() const { return _processEntities; } Whitespace WhitespaceMode() const { return _whitespace; } /** Returns true if this document has a leading Byte Order Mark of UTF8. */ bool HasBOM() const { return _writeBOM; } /** Sets whether to write the BOM when writing the file. */ void SetBOM( bool useBOM ) { _writeBOM = useBOM; } /** Return the root element of DOM. Equivalent to FirstChildElement(). To get the first node, use FirstChild(). */ XMLElement* RootElement() { return FirstChildElement(); } const XMLElement* RootElement() const { return FirstChildElement(); } /** Print the Document. If the Printer is not provided, it will print to stdout. If you provide Printer, this can print to a file: @verbatim XMLPrinter printer( fp ); doc.Print( &printer ); @endverbatim Or you can use a printer to print to memory: @verbatim XMLPrinter printer; doc->Print( &printer ); // printer.CStr() has a const char* to the XML @endverbatim */ void Print( XMLPrinter* streamer=0 ); virtual bool Accept( XMLVisitor* visitor ) const; /** Create a new Element associated with this Document. The memory for the Element is managed by the Document. */ XMLElement* NewElement( const char* name ); /** Create a new Comment associated with this Document. The memory for the Comment is managed by the Document. */ XMLComment* NewComment( const char* comment ); /** Create a new Text associated with this Document. The memory for the Text is managed by the Document. */ XMLText* NewText( const char* text ); /** Create a new Declaration associated with this Document. The memory for the object is managed by the Document. If the 'text' param is null, the standard declaration is used.: @verbatim @endverbatim */ XMLDeclaration* NewDeclaration( const char* text=0 ); /** Create a new Unknown associated with this Document. The memory forthe object is managed by the Document. */ XMLUnknown* NewUnknown( const char* text ); /** Delete a node associated with this document. It will be unlinked from the DOM. */ void DeleteNode( XMLNode* node ) { node->_parent->DeleteChild( node ); } void SetError( XMLError error, const char* str1, const char* str2 ); /// Return true if there was an error parsing the document. bool Error() const { return _errorID != XML_NO_ERROR; } /// Return the errorID. XMLError ErrorID() const { return _errorID; } /// Return a possibly helpful diagnostic location or string. const char* GetErrorStr1() const { return _errorStr1; } /// Return a possibly helpful secondary diagnostic location or string. const char* GetErrorStr2() const { return _errorStr2; } /// If there is an error, print it to stdout. void PrintError() const; // internal char* Identify( char* p, XMLNode** node ); virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { return 0; } virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { return false; } private: XMLDocument( const XMLDocument& ); // not supported void operator=( const XMLDocument& ); // not supported void InitDocument(); bool _writeBOM; bool _processEntities; XMLError _errorID; Whitespace _whitespace; const char* _errorStr1; const char* _errorStr2; char* _charBuffer; MemPoolT< sizeof(XMLElement) > _elementPool; MemPoolT< sizeof(XMLAttribute) > _attributePool; MemPoolT< sizeof(XMLText) > _textPool; MemPoolT< sizeof(XMLComment) > _commentPool; }; /** A XMLHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. Note that XMLHandle is not part of the TinyXML DOM structure. It is a separate utility class. Take an example: @verbatim @endverbatim Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very easy to write a *lot* of code that looks like: @verbatim XMLElement* root = document.FirstChildElement( "Document" ); if ( root ) { XMLElement* element = root->FirstChildElement( "Element" ); if ( element ) { XMLElement* child = element->FirstChildElement( "Child" ); if ( child ) { XMLElement* child2 = child->NextSiblingElement( "Child" ); if ( child2 ) { // Finally do something useful. @endverbatim And that doesn't even cover "else" cases. XMLHandle addresses the verbosity of such code. A XMLHandle checks for null pointers so it is perfectly safe and correct to use: @verbatim XMLHandle docHandle( &document ); XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement(); if ( child2 ) { // do something useful @endverbatim Which is MUCH more concise and useful. It is also safe to copy handles - internally they are nothing more than node pointers. @verbatim XMLHandle handleCopy = handle; @endverbatim See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. */ class XMLHandle { public: /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. XMLHandle( XMLNode* node ) { _node = node; } /// Create a handle from a node. XMLHandle( XMLNode& node ) { _node = &node; } /// Copy constructor XMLHandle( const XMLHandle& ref ) { _node = ref._node; } /// Assignment XMLHandle& operator=( const XMLHandle& ref ) { _node = ref._node; return *this; } /// Get the first child of this handle. XMLHandle FirstChild() { return XMLHandle( _node ? _node->FirstChild() : 0 ); } /// Get the first child element of this handle. XMLHandle FirstChildElement( const char* value=0 ) { return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); } /// Get the last child of this handle. XMLHandle LastChild() { return XMLHandle( _node ? _node->LastChild() : 0 ); } /// Get the last child element of this handle. XMLHandle LastChildElement( const char* _value=0 ) { return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); } /// Get the previous sibling of this handle. XMLHandle PreviousSibling() { return XMLHandle( _node ? _node->PreviousSibling() : 0 ); } /// Get the previous sibling element of this handle. XMLHandle PreviousSiblingElement( const char* _value=0 ) { return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); } /// Get the next sibling of this handle. XMLHandle NextSibling() { return XMLHandle( _node ? _node->NextSibling() : 0 ); } /// Get the next sibling element of this handle. XMLHandle NextSiblingElement( const char* _value=0 ) { return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); } /// Safe cast to XMLNode. This can return null. XMLNode* ToNode() { return _node; } /// Safe cast to XMLElement. This can return null. XMLElement* ToElement() { return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); } /// Safe cast to XMLText. This can return null. XMLText* ToText() { return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); } /// Safe cast to XMLUnknown. This can return null. XMLUnknown* ToUnknown() { return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); } /// Safe cast to XMLDeclaration. This can return null. XMLDeclaration* ToDeclaration() { return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); } private: XMLNode* _node; }; /** A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the same in all regards, except for the 'const' qualifiers. See XMLHandle for API. */ class XMLConstHandle { public: XMLConstHandle( const XMLNode* node ) { _node = node; } XMLConstHandle( const XMLNode& node ) { _node = &node; } XMLConstHandle( const XMLConstHandle& ref ) { _node = ref._node; } XMLConstHandle& operator=( const XMLConstHandle& ref ) { _node = ref._node; return *this; } const XMLConstHandle FirstChild() const { return XMLConstHandle( _node ? _node->FirstChild() : 0 ); } const XMLConstHandle FirstChildElement( const char* value=0 ) const { return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); } const XMLConstHandle LastChild() const { return XMLConstHandle( _node ? _node->LastChild() : 0 ); } const XMLConstHandle LastChildElement( const char* _value=0 ) const { return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); } const XMLConstHandle PreviousSibling() const { return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); } const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); } const XMLConstHandle NextSibling() const { return XMLConstHandle( _node ? _node->NextSibling() : 0 ); } const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); } const XMLNode* ToNode() const { return _node; } const XMLElement* ToElement() const { return ( ( _node && _node->ToElement() ) ? _node->ToElement() : 0 ); } const XMLText* ToText() const { return ( ( _node && _node->ToText() ) ? _node->ToText() : 0 ); } const XMLUnknown* ToUnknown() const { return ( ( _node && _node->ToUnknown() ) ? _node->ToUnknown() : 0 ); } const XMLDeclaration* ToDeclaration() const { return ( ( _node && _node->ToDeclaration() ) ? _node->ToDeclaration() : 0 ); } private: const XMLNode* _node; }; /** Printing functionality. The XMLPrinter gives you more options than the XMLDocument::Print() method. It can: -# Print to memory. -# Print to a file you provide. -# Print XML without a XMLDocument. Print to Memory @verbatim XMLPrinter printer; doc->Print( &printer ); SomeFunction( printer.CStr() ); @endverbatim Print to a File You provide the file pointer. @verbatim XMLPrinter printer( fp ); doc.Print( &printer ); @endverbatim Print without a XMLDocument When loading, an XML parser is very useful. However, sometimes when saving, it just gets in the way. The code is often set up for streaming, and constructing the DOM is just overhead. The Printer supports the streaming case. The following code prints out a trivially simple XML file without ever creating an XML document. @verbatim XMLPrinter printer( fp ); printer.OpenElement( "foo" ); printer.PushAttribute( "foo", "bar" ); printer.CloseElement(); @endverbatim */ class XMLPrinter : public XMLVisitor { public: /** Construct the printer. If the FILE* is specified, this will print to the FILE. Else it will print to memory, and the result is available in CStr(). If 'compact' is set to true, then output is created with only required whitespace and newlines. */ XMLPrinter( FILE* file=0, bool compact = false ); ~XMLPrinter() {} /** If streaming, write the BOM and declaration. */ void PushHeader( bool writeBOM, bool writeDeclaration ); /** If streaming, start writing an element. The element must be closed with CloseElement() */ void OpenElement( const char* name ); /// If streaming, add an attribute to an open element. void PushAttribute( const char* name, const char* value ); void PushAttribute( const char* name, int value ); void PushAttribute( const char* name, unsigned value ); void PushAttribute( const char* name, bool value ); void PushAttribute( const char* name, double value ); /// If streaming, close the Element. void CloseElement(); /// Add a text node. void PushText( const char* text, bool cdata=false ); /// Add a text node from an integer. void PushText( int value ); /// Add a text node from an unsigned. void PushText( unsigned value ); /// Add a text node from a bool. void PushText( bool value ); /// Add a text node from a float. void PushText( float value ); /// Add a text node from a double. void PushText( double value ); /// Add a comment void PushComment( const char* comment ); void PushDeclaration( const char* value ); void PushUnknown( const char* value ); virtual bool VisitEnter( const XMLDocument& /*doc*/ ); virtual bool VisitExit( const XMLDocument& /*doc*/ ) { return true; } virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); virtual bool VisitExit( const XMLElement& element ); virtual bool Visit( const XMLText& text ); virtual bool Visit( const XMLComment& comment ); virtual bool Visit( const XMLDeclaration& declaration ); virtual bool Visit( const XMLUnknown& unknown ); /** If in print to memory mode, return a pointer to the XML file in memory. */ const char* CStr() const { return _buffer.Mem(); } /** If in print to memory mode, return the size of the XML file in memory. (Note the size returned includes the terminating null.) */ int CStrSize() const { return _buffer.Size(); } private: void SealElement(); void PrintSpace( int depth ); void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. void Print( const char* format, ... ); bool _elementJustOpened; bool _firstElement; FILE* _fp; int _depth; int _textDepth; bool _processEntities; bool _compactMode; enum { ENTITY_RANGE = 64, BUF_SIZE = 200 }; bool _entityFlag[ENTITY_RANGE]; bool _restrictedEntityFlag[ENTITY_RANGE]; DynArray< const char*, 10 > _stack; DynArray< char, 20 > _buffer; #ifdef _MSC_VER DynArray< char, 20 > _accumulator; #endif }; } // tinyxml2 #endif // TINYXML2_INCLUDED \ No newline at end of file diff --git a/modules/oculus_sdk_mac/Firmware/RiftTrackerFirmware_v0.18rc.ovrf b/modules/oculus_sdk_mac/Firmware/RiftTrackerFirmware_v0.18rc.ovrf new file mode 100644 index 0000000..090ce41 Binary files /dev/null and b/modules/oculus_sdk_mac/Firmware/RiftTrackerFirmware_v0.18rc.ovrf differ diff --git a/modules/oculus_sdk_mac/Firmware/readme.txt b/modules/oculus_sdk_mac/Firmware/readme.txt new file mode 100644 index 0000000..d42d090 --- /dev/null +++ b/modules/oculus_sdk_mac/Firmware/readme.txt @@ -0,0 +1 @@ +Firmware packages for Oculus products are stored in this folder. Use the OculusConfigUtil to load them and update your products. Tracker DK Firmware Version History =================================== 0.18 - 2013-10-03 * Resolve a calibration issue that could result in additional drift in certain conditions. * Resolve a calibration issue that resulted in additional drift after fast motions. 0.17 - 2013-04-24 * Fix an issue where the Tracker can fail to initialize with Logitech software running. 0.16 - 2013-03-13 * Release firmware for the Oculus Rift DK. \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LICENSE.txt b/modules/oculus_sdk_mac/LICENSE.txt new file mode 100644 index 0000000..c0fc015 --- /dev/null +++ b/modules/oculus_sdk_mac/LICENSE.txt @@ -0,0 +1 @@ +Oculus VR Rift SDK Software License Oculus VR, Inc. Software Development Kit License Agreement Copyright © 2014 Oculus VR, Inc. All rights reserved. The text of this may be found at: http://www.oculusvr.com/licenses/LICENSE-3.1 Human-Readable Summary*: You are Free to: Use, modify, and distribute the Oculus VR Rift SDK in source and binary form with your applications/software. With the Following Restrictions: You can only distribute or re-distribute the source code to LibOVR in whole, not in part. Modifications to the Oculus VR Rift SDK in source or binary form must be shared with Oculus VR. If your applications cause health and safety issues, you may lose your right to use the Oculus VR Rift SDK, including LibOVR. The Oculus VR Rift SDK may not be used to interface with unapproved commercial virtual reality mobile or non-mobile products or hardware. * - This human-readable Summary is not a license. It is simply a convenient reference for understanding the full Oculus VR Rift SDK License Agreement. The Summary is written as a user-friendly interface to the full Oculus VR Rift SDK License below. This Summary itself has no legal value, and its contents do not appear in the actual license. Full-length Legal Copy: 1. Subject to the terms and conditions of this License Agreement (the "License"), Oculus VR, Inc. ("Oculus VR") hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, sublicenseable copyright license to use, reproduce, redistribute (subject to restrictions below), modify, and improve the software contained in this Oculus VR Rift Software Development Kit ("RIFT SDK"), including, but not limited to, the samples, headers, LibOVR headers, and LibOVR source. This license is subject to the following terms and conditions: 1.1. This license includes the non-exclusive license and right to use (i) the RIFT SDK to make applications, content, games and demos (collectively and generally referred to as “Developer Content”) that run on the Oculus VR approved mobile hardware and software products (“Oculus Approved Rift Products”) and which may incorporate the RIFT SDK in whole or in part in binary or object code; and (ii) to use the RIFT SDK to create derivative works of the RIFT SDK itself ("RIFT SDK Derivatives"), whether in source, binary, or object form, in whole or in part, including third party software unless otherwise noted. 1.2. RIFT SDK Derivatives are further defined as source, binary or object code derived exclusively from the RIFT SDK by you; provided, however, that RIFT SDK Derivatives do not include the Developer Content (engines, utilities, applications, content, games or demos) which may be developed using the RIFT SDK. By way of example a mobile application or game or demo that is developed using the RIFT SDK would not be a RIFT SDK Derivative , nor would a utility or tool set in a pre-existing game engine that is adapted to work with the RIFT SDK be a RIFT SDK Derivative. By way of example, but not limitation, a RIFT SDK Derivative is or would be: either (i) an adaptation of a utility or piece of code from the RIFT SDK to improve efficiency; or (ii) an addition of code or improvement to the RIFT SDK that adds functionality. 1.3 For the sake of clarification when you use the RIFT SDK (including RIFT SDK Derivatives) in or with Developer Content, you retain all rights to your Developer Content, and you have no obligations to share or license Developer Content (including your source and object code) to Oculus VR or any third parties; provided, however, Oculus VR retains all rights to the RIFT SDK and the RIFT SDK Derivatives that may be incorporated into your Developer Content. 1.4 You agree to and you will use the Flash Screen Warning and the Health and Safety Warnings (collectively the “Oculus Warnings”) and the Oculus VR health and safety protocols found in the Oculus Best Practices Guide (“Oculus H&S Protocols”), and your use of the Oculus Warnings and the Oculus end user license agreement (“Oculus EULA”) with your Developer Content as provided for in the Oculus Developer Center, all of which can be found at the following link: https://developer.oculusvr.com/?action=doc. 2. You, the recipient and user of the RIFT SDK, hereby agree and accept that that Oculus VR shall own all right, title and interest to the intellectual property rights, including, but limited to copyright, trademark and patent rights, to any RIFT SDK Derivatives that you may create, and you hereby assign any and all such rights to such RIFT SDK Derivatives to Oculus VR, subject to the following. 2.1 We hereby grant to you the a fully paid up, no-charge, royalty-free, world-wide, in perpetuity, non-exclusive right and license back to use these RIFT SDK Derivatives solely in conjunction with the RIFT SDK (or any components of the RIFT SDK) and/or Developer Content on Oculus Rift Products as set forth herein. 2.2 Furthermore, for the sake of clarification, Oculus VR and its assignees and licensees shall be free to use such RIFT SDK Derivatives without any approval from you and without compensation or attribution to you. 2.3 You also agree upon Oculus VR's request to provide the source and binary code of any RIFT SDK Derivatives to Oculus VR. FAILURE TO COMPLY WITH THIS REQUEST IS THE BASIS FOR AUTOMATIC TERMINATION OF THIS LICENSE BY OCULUS VR. 3. Subject to the terms and conditions of this License, your license to redistribute and sublicense the RIFT SDK and RIFT SDK Derivatives is also expressly made subject to the following conditions: 3.1. You may sublicense and redistribute the source, binary, or object code of the RIFT SDK in whole or in part by itself for no charge or as part of a for charge piece of Developer Content; provided, however, you may only license, sublicense or redistribute the source, binary or object code of LibOVR in whole, and you may not license, sublicense or redistribute any portion or element of LibOVR separately or in part (in either source, binary or object form). If you license, sublicense or redistribute RIFT SDK Derivatives in and of themselves (not as a part of a piece of Developer Content) then you may only do that solely with and in conjunction with either the RIFT SDK or LibOVR. The RIFT SDK (including, but not limited to LibOVR), any RIFT SDK Derivatives, and any Developer Content may only be used with Oculus Approved Rift Products and may not be used, licensed, or sublicensed to interface with mobile software or hardware or other commercial headsets, mobile tablets or phones that are not authorized and approved by Oculus VR; 3.2. You must include with all such redistributed or sublicensed RIFT SDK or RIFT SDK Derivatives code the following copyright notice: "Copyright © 2014 Oculus VR, Inc. All rights reserved," and include the list of conditions contained in this Section 3, including the full text of the disclaimer in Section 3.6 below; 3.3. Neither the name of Oculus VR, Inc. nor the names of Oculus VR, Inc.'s contributors, licensors, employees, or contractors, may be used to endorse or promote products derived from this RIFT SDK without specific prior written permission of Oculus VR, Inc.; 3.4. You must give any other recipients of the RIFT SDK or any elements thereof, including LibOVR or RIFT SDK Derivatives, a copy of this License as such recipients, licensees or sublicensees may only use the RIFT SDK or any RIFT SDK Derivatives or any elements thereof subject to the terms of this Licence and such recipients, licensees or sublicensees agreement and acceptance of this License with Oculus VR (which will convey all rights to the recipients’ or licensees’ or sublicensees’ RIFT SDK Derivatives to Oculus VR), and you must cause any modified files to carry prominent notices stating that you changed the files; 3.5. If the RIFT SDK or a specific element thereof such as LibOVR includes a "LICENSE" text file as part of its distribution (the “License Notice”), then any RIFT SDK Derivatives that you distribute with the RIFT SDK in whole or in part must include a readable copy of such attribution notices as are contained within the applicable License Notice file (excluding those notices that do not pertain to any part of the RIFT SDK Derivatives), in at least one of the following places: within a License Notice text file distributed as part of the RIFT SDK Derivatives; within the source form or documentation, if provided along with the RIFT SDK Derivatives; or, within a display generated by the RIFT SDK Derivatives, if and wherever such third-party notices normally appear. You must also include in the License Notice file for all RIFT SDK Derivatives a copy of all notices (including any product liability or health and safety notices). The contents of the License Notice file are for informational purposes only and do not modify the License. You may add your own attribution notices within RIFT SDK Derivatives that you distribute, alongside or as an addendum to the License Notice text from the RIFT SDK or any part thereof, provided that such additional attribution notices cannot be construed as modifying the License. 3.6. THIS RIFT SDK AND ANY COMPONENT THEREOF IS PROVIDED BY OCULUS VR AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OCULUS VR AS THE COPYRIGHT OWNER OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS RIFT SDK OR THE RIFT SDK DERIVATIVES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4. This License does not grant permission to use the trade names, trademarks, service marks, or product names of Oculus VR, except as required for reasonable and customary use in describing the origin of the RIFT SDK, LibOVR, or any element thereof, and reproducing the content of the License Notice file. 5. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall Oculus VR or any contributor be liable to you or your licensees or sublicensees for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the RIFT SDK, LibOVR, any element thereof or any RIFT SDK Derivatives (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if you or such contributor has been advised of the possibility of such damages. 6. Your acceptance of the terms and conditions of this License in and of itself and for all Developer Content created as of March 1, 2014, may be evidenced by any of the following: your usage of the RIFT SDK or any element thereof, acceptance of the click-through agreement, or opening the packaging of the CD-ROM containing the RIFT SDK or any element thereof, including LibOVR. As this License is updated for future releases of the RIFT SDK and/or LibOVR, you agree to abide by and meet all requirements of future updates of this License for those future RIFT SDK releases as evidenced by the same usage of the RIFT SDK or any element thereof and the future updates of this License will apply for that future Developer Content that may developed for or with that future RIFT SDK or any element thereof (i.e., you cannot sidestep out of the requirements of future updates of the License by developing against an older release of the RIFT SDK or License). 7. Oculus VR reserves the right to terminate this License and all your rights hereunder in the event you materially breach this License and fail to cure such breach within ten (10) business days after notice of breach from Oculus VR. 8. Furthermore, Oculus VR also reserves the right to cancel or terminate this License for any of the following reasons upon notice to you, subject to the appeal process set forth in Section 14 for a wrongful termination: a) Intellectual property infringement by you with Developer Content or RIFT SDK Derivatives created by you that is used with or by the RIFT SDK or any part thereof, or any of the RIFT SDK Derivatives; b) Developer Content that violates or infringes upon applicable law; c) Health and safety issues associated with your Developer Content; d) Failure to comply with or use properly the Oculus Warnings, Oculus H&S Protocols, or Oculus EULA; e) Use of the RIFT SDK, RIFT SDK Derivatives or LibOVR with a commercial product other than an Oculus Approved Product; and f) Failure to provide required notices or deliver source code and/or binary of RIFT SDK Derivatives as set forth above. If you believe that you have been wrongfully terminated under this Section 8 with respect to material breach or with respect to these above conditions, you have the right to appeal the termination of this License under Section 14. 9. This License may be amended by Oculus VR on a prospective basis, and your usage of the License after such amendments or changes signifies your consent to and acceptance of any such amendments or changes on a going forward basis. 10. In the event any provision of this License is determined to be invalid, prohibited or unenforceable by a court or other body of competent jurisdiction, this License shall be construed as if such invalid, prohibited or unenforceable provision has been more narrowly drawn so as not to be invalid, prohibited or unenforceable. 11. You may not assign any rights or obligations under this License without the advance written consent of Oculus VR, which may be withheld in its sole discretion. Oculus VR may assign its rights or obligations under this License in its sole discretion. 12. Failure of either party at any time to enforce any of the provisions of this License will not be construed as a waiver of such provisions or in any way affect the validity of this License or parts thereof. 13. Your remedies under this License shall be limited to the right to collect money damages, if any, and you hereby waive your right to injunctive or other equitable relief. 14. This License shall be governed by the laws of the State of California, without giving effect to choice of law principles. All disputes relating to this License shall be resolved by binding non-appearance-based arbitration before a neutral arbitrator in Orange County, California. If your License has been terminated hereunder by Oculus, you may appeal your termination through this arbitration process on an expedited basis with an arbitration within thirty days of your giving Oculus VR notice of the appeal. The arbitration shall be conducted in accordance with the rules and procedures of JAMS then in effect, and the judgment of the arbitrator shall be final and capable of entry in any court of competent jurisdiction. You agree to submit to the personal jurisdiction of the courts located within Orange County, California in connection with any entrance of an arbitrator’s judgment or decision or any dispute with respect to the arbitration process or procedure or Oculus VR’s exercise of its equitable rights or remedies. \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Include/OVR.h b/modules/oculus_sdk_mac/LibOVR/Include/OVR.h new file mode 100644 index 0000000..d188184 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Include/OVR.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR.h Content : This contains references to all OVR-specific headers in Src folder. Should be generated automatically based on PublicHeader tags. Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_h #define OVR_h #include "../Src/Kernel/OVR_Allocator.h" #include "../Src/Kernel/OVR_Log.h" #include "../Src/Kernel/OVR_Math.h" #include "../Src/Kernel/OVR_System.h" #include "../Src/Kernel/OVR_Types.h" #include "../Src/OVR_Device.h" #include "../Src/OVR_DeviceConstants.h" #include "../Src/OVR_DeviceHandle.h" #include "../Src/OVR_DeviceMessages.h" #include "../Src/OVR_SensorFusion.h" #include "../Src/OVR_Stereo.h" #include "../Src/OVR_Profile.h" #include "../Src/Util/Util_LatencyTest.h" #include "../Src/Util/Util_Render_Stereo.h" #include "../Src/Util/Util_Interface.h" #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Include/OVRVersion.h b/modules/oculus_sdk_mac/LibOVR/Include/OVRVersion.h new file mode 100644 index 0000000..a7bc432 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Include/OVRVersion.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVRVersion.h Content : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef _OVR_VERSION_H #define _OVR_VERSION_H #define OVR_MAJOR_VERSION 0 #define OVR_MINOR_VERSION 3 #define OVR_BUILD_VERSION 2 #define OVR_VERSION_STRING "0.3.2" #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Debug/libovr.a b/modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Debug/libovr.a new file mode 100644 index 0000000..c384fef Binary files /dev/null and b/modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Debug/libovr.a differ diff --git a/modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a b/modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a new file mode 100644 index 0000000..1b4ed32 Binary files /dev/null and b/modules/oculus_sdk_mac/LibOVR/Lib/MacOS/Release/libovr.a differ diff --git a/modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj/project.pbxproj b/modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj/project.pbxproj new file mode 100644 index 0000000..485af04 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj/project.pbxproj @@ -0,0 +1,731 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E8754F81190F1B71005FD401 /* OVR_Recording.h in Headers */ = {isa = PBXBuildFile; fileRef = E8754F7F190F1B71005FD401 /* OVR_Recording.h */; }; + E886FE9E190737FA00D5DB45 /* OVR_CAPI_GL.h in Headers */ = {isa = PBXBuildFile; fileRef = E886FE9B190737FA00D5DB45 /* OVR_CAPI_GL.h */; }; + E886FE9F190737FA00D5DB45 /* OVR_CAPI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E886FE9C190737FA00D5DB45 /* OVR_CAPI.cpp */; }; + E886FEA0190737FA00D5DB45 /* OVR_CAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = E886FE9D190737FA00D5DB45 /* OVR_CAPI.h */; }; + E886FEA21907528C00D5DB45 /* CAPI_DistortionRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E886FEA11907528C00D5DB45 /* CAPI_DistortionRenderer.cpp */; }; + E8AA40D11907221900D5F144 /* OVR_Alg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40A61907221900D5F144 /* OVR_Alg.cpp */; }; + E8AA40D21907221900D5F144 /* OVR_Alg.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40A71907221900D5F144 /* OVR_Alg.h */; }; + E8AA40D31907221900D5F144 /* OVR_Allocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40A81907221900D5F144 /* OVR_Allocator.cpp */; }; + E8AA40D41907221900D5F144 /* OVR_Allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40A91907221900D5F144 /* OVR_Allocator.h */; }; + E8AA40D51907221900D5F144 /* OVR_Array.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40AA1907221900D5F144 /* OVR_Array.h */; }; + E8AA40D61907221900D5F144 /* OVR_Atomic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40AB1907221900D5F144 /* OVR_Atomic.cpp */; }; + E8AA40D71907221900D5F144 /* OVR_Atomic.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40AC1907221900D5F144 /* OVR_Atomic.h */; }; + E8AA40D81907221900D5F144 /* OVR_Color.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40AD1907221900D5F144 /* OVR_Color.h */; }; + E8AA40D91907221900D5F144 /* OVR_ContainerAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40AE1907221900D5F144 /* OVR_ContainerAllocator.h */; }; + E8AA40DA1907221900D5F144 /* OVR_Deque.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40AF1907221900D5F144 /* OVR_Deque.h */; }; + E8AA40DB1907221900D5F144 /* OVR_File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40B01907221900D5F144 /* OVR_File.cpp */; }; + E8AA40DC1907221900D5F144 /* OVR_File.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40B11907221900D5F144 /* OVR_File.h */; }; + E8AA40DD1907221900D5F144 /* OVR_FileFILE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40B21907221900D5F144 /* OVR_FileFILE.cpp */; }; + E8AA40DE1907221900D5F144 /* OVR_Hash.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40B31907221900D5F144 /* OVR_Hash.h */; }; + E8AA40DF1907221900D5F144 /* OVR_KeyCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40B41907221900D5F144 /* OVR_KeyCodes.h */; }; + E8AA40E01907221900D5F144 /* OVR_List.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40B51907221900D5F144 /* OVR_List.h */; }; + E8AA40E11907221900D5F144 /* OVR_Lockless.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40B61907221900D5F144 /* OVR_Lockless.cpp */; }; + E8AA40E21907221900D5F144 /* OVR_Lockless.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40B71907221900D5F144 /* OVR_Lockless.h */; }; + E8AA40E31907221900D5F144 /* OVR_Log.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40B81907221900D5F144 /* OVR_Log.cpp */; }; + E8AA40E41907221900D5F144 /* OVR_Log.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40B91907221900D5F144 /* OVR_Log.h */; }; + E8AA40E51907221900D5F144 /* OVR_Math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40BA1907221900D5F144 /* OVR_Math.cpp */; }; + E8AA40E61907221900D5F144 /* OVR_Math.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40BB1907221900D5F144 /* OVR_Math.h */; }; + E8AA40E71907221900D5F144 /* OVR_RefCount.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40BC1907221900D5F144 /* OVR_RefCount.cpp */; }; + E8AA40E81907221900D5F144 /* OVR_RefCount.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40BD1907221900D5F144 /* OVR_RefCount.h */; }; + E8AA40E91907221900D5F144 /* OVR_Std.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40BE1907221900D5F144 /* OVR_Std.cpp */; }; + E8AA40EA1907221900D5F144 /* OVR_Std.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40BF1907221900D5F144 /* OVR_Std.h */; }; + E8AA40EB1907221900D5F144 /* OVR_String.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40C01907221900D5F144 /* OVR_String.cpp */; }; + E8AA40EC1907221900D5F144 /* OVR_String.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40C11907221900D5F144 /* OVR_String.h */; }; + E8AA40ED1907221900D5F144 /* OVR_String_FormatUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40C21907221900D5F144 /* OVR_String_FormatUtil.cpp */; }; + E8AA40EE1907221900D5F144 /* OVR_String_PathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40C31907221900D5F144 /* OVR_String_PathUtil.cpp */; }; + E8AA40EF1907221900D5F144 /* OVR_StringHash.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40C41907221900D5F144 /* OVR_StringHash.h */; }; + E8AA40F01907221900D5F144 /* OVR_SysFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40C51907221900D5F144 /* OVR_SysFile.cpp */; }; + E8AA40F11907221900D5F144 /* OVR_SysFile.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40C61907221900D5F144 /* OVR_SysFile.h */; }; + E8AA40F21907221900D5F144 /* OVR_System.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40C71907221900D5F144 /* OVR_System.cpp */; }; + E8AA40F31907221900D5F144 /* OVR_System.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40C81907221900D5F144 /* OVR_System.h */; }; + E8AA40F41907221900D5F144 /* OVR_Threads.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40C91907221900D5F144 /* OVR_Threads.h */; }; + E8AA40F51907221900D5F144 /* OVR_ThreadsPthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40CA1907221900D5F144 /* OVR_ThreadsPthread.cpp */; }; + E8AA40F71907221900D5F144 /* OVR_Timer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40CC1907221900D5F144 /* OVR_Timer.cpp */; }; + E8AA40F81907221900D5F144 /* OVR_Timer.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40CD1907221900D5F144 /* OVR_Timer.h */; }; + E8AA40F91907221900D5F144 /* OVR_Types.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40CE1907221900D5F144 /* OVR_Types.h */; }; + E8AA40FA1907221900D5F144 /* OVR_UTF8Util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA40CF1907221900D5F144 /* OVR_UTF8Util.cpp */; }; + E8AA40FB1907221900D5F144 /* OVR_UTF8Util.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA40D01907221900D5F144 /* OVR_UTF8Util.h */; }; + E8AA410D1907224700D5F144 /* Util_Interface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41011907224700D5F144 /* Util_Interface.cpp */; }; + E8AA410E1907224700D5F144 /* Util_Interface.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41021907224700D5F144 /* Util_Interface.h */; }; + E8AA410F1907224700D5F144 /* Util_LatencyTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41031907224700D5F144 /* Util_LatencyTest.cpp */; }; + E8AA41101907224700D5F144 /* Util_LatencyTest.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41041907224700D5F144 /* Util_LatencyTest.h */; }; + E8AA41111907224700D5F144 /* Util_LatencyTest2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41051907224700D5F144 /* Util_LatencyTest2.cpp */; }; + E8AA41121907224700D5F144 /* Util_LatencyTest2.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41061907224700D5F144 /* Util_LatencyTest2.h */; }; + E8AA41131907224700D5F144 /* Util_Render_Stereo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41071907224700D5F144 /* Util_Render_Stereo.cpp */; }; + E8AA41141907224700D5F144 /* Util_Render_Stereo.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41081907224700D5F144 /* Util_Render_Stereo.h */; }; + E8AA4169190722BB00D5F144 /* OVR_Device.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4121190722BB00D5F144 /* OVR_Device.h */; }; + E8AA416A190722BB00D5F144 /* OVR_DeviceConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4122190722BB00D5F144 /* OVR_DeviceConstants.h */; }; + E8AA416B190722BB00D5F144 /* OVR_DeviceHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4123190722BB00D5F144 /* OVR_DeviceHandle.cpp */; }; + E8AA416C190722BB00D5F144 /* OVR_DeviceHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4124190722BB00D5F144 /* OVR_DeviceHandle.h */; }; + E8AA416D190722BB00D5F144 /* OVR_DeviceImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4125190722BB00D5F144 /* OVR_DeviceImpl.cpp */; }; + E8AA416E190722BB00D5F144 /* OVR_DeviceImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4126190722BB00D5F144 /* OVR_DeviceImpl.h */; }; + E8AA416F190722BB00D5F144 /* OVR_DeviceMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4127190722BB00D5F144 /* OVR_DeviceMessages.h */; }; + E8AA4170190722BB00D5F144 /* OVR_HIDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4128190722BB00D5F144 /* OVR_HIDDevice.h */; }; + E8AA4171190722BB00D5F144 /* OVR_HIDDeviceBase.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4129190722BB00D5F144 /* OVR_HIDDeviceBase.h */; }; + E8AA4172190722BB00D5F144 /* OVR_HIDDeviceImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA412A190722BB00D5F144 /* OVR_HIDDeviceImpl.h */; }; + E8AA4173190722BB00D5F144 /* OVR_JSON.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA412B190722BB00D5F144 /* OVR_JSON.cpp */; }; + E8AA4174190722BB00D5F144 /* OVR_JSON.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA412C190722BB00D5F144 /* OVR_JSON.h */; }; + E8AA4175190722BB00D5F144 /* OVR_LatencyTestImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA412D190722BB00D5F144 /* OVR_LatencyTestImpl.cpp */; }; + E8AA4176190722BB00D5F144 /* OVR_LatencyTestImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA412E190722BB00D5F144 /* OVR_LatencyTestImpl.h */; }; + E8AA417E190722BB00D5F144 /* OVR_OSX_DeviceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4136190722BB00D5F144 /* OVR_OSX_DeviceManager.cpp */; }; + E8AA417F190722BB00D5F144 /* OVR_OSX_DeviceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4137190722BB00D5F144 /* OVR_OSX_DeviceManager.h */; }; + E8AA4180190722BB00D5F144 /* OVR_OSX_HIDDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4138190722BB00D5F144 /* OVR_OSX_HIDDevice.cpp */; }; + E8AA4181190722BB00D5F144 /* OVR_OSX_HIDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4139190722BB00D5F144 /* OVR_OSX_HIDDevice.h */; }; + E8AA4182190722BB00D5F144 /* OVR_OSX_HMDDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA413A190722BB00D5F144 /* OVR_OSX_HMDDevice.cpp */; }; + E8AA4183190722BB00D5F144 /* OVR_OSX_HMDDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA413B190722BB00D5F144 /* OVR_OSX_HMDDevice.h */; }; + E8AA4184190722BB00D5F144 /* OVR_OSX_SensorDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA413C190722BB00D5F144 /* OVR_OSX_SensorDevice.cpp */; }; + E8AA4185190722BB00D5F144 /* OVR_Profile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA413D190722BB00D5F144 /* OVR_Profile.cpp */; }; + E8AA4186190722BB00D5F144 /* OVR_Profile.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA413E190722BB00D5F144 /* OVR_Profile.h */; }; + E8AA4187190722BB00D5F144 /* OVR_Sensor2Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA413F190722BB00D5F144 /* OVR_Sensor2Impl.cpp */; }; + E8AA4188190722BB00D5F144 /* OVR_Sensor2Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4140190722BB00D5F144 /* OVR_Sensor2Impl.h */; }; + E8AA4189190722BB00D5F144 /* OVR_Sensor2ImplUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4141190722BB00D5F144 /* OVR_Sensor2ImplUtil.h */; }; + E8AA418A190722BB00D5F144 /* OVR_SensorCalibration.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4142190722BB00D5F144 /* OVR_SensorCalibration.cpp */; }; + E8AA418B190722BB00D5F144 /* OVR_SensorCalibration.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4143190722BB00D5F144 /* OVR_SensorCalibration.h */; }; + E8AA418C190722BB00D5F144 /* OVR_SensorFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4144190722BB00D5F144 /* OVR_SensorFilter.cpp */; }; + E8AA418D190722BB00D5F144 /* OVR_SensorFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4145190722BB00D5F144 /* OVR_SensorFilter.h */; }; + E8AA418E190722BB00D5F144 /* OVR_SensorFusion.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4146190722BB00D5F144 /* OVR_SensorFusion.cpp */; }; + E8AA418F190722BB00D5F144 /* OVR_SensorFusion.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4147190722BB00D5F144 /* OVR_SensorFusion.h */; }; + E8AA4190190722BB00D5F144 /* OVR_SensorFusionDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4148190722BB00D5F144 /* OVR_SensorFusionDebug.h */; }; + E8AA4191190722BB00D5F144 /* OVR_SensorImpl_Common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4149190722BB00D5F144 /* OVR_SensorImpl_Common.cpp */; }; + E8AA4192190722BB00D5F144 /* OVR_SensorImpl_Common.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA414A190722BB00D5F144 /* OVR_SensorImpl_Common.h */; }; + E8AA4193190722BB00D5F144 /* OVR_SensorImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA414B190722BB00D5F144 /* OVR_SensorImpl.cpp */; }; + E8AA4194190722BB00D5F144 /* OVR_SensorImpl.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA414C190722BB00D5F144 /* OVR_SensorImpl.h */; }; + E8AA4195190722BB00D5F144 /* OVR_SensorTimeFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA414D190722BB00D5F144 /* OVR_SensorTimeFilter.cpp */; }; + E8AA4196190722BB00D5F144 /* OVR_SensorTimeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA414E190722BB00D5F144 /* OVR_SensorTimeFilter.h */; }; + E8AA4197190722BB00D5F144 /* OVR_Stereo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA414F190722BB00D5F144 /* OVR_Stereo.cpp */; }; + E8AA4198190722BB00D5F144 /* OVR_Stereo.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4150190722BB00D5F144 /* OVR_Stereo.h */; }; + E8AA4199190722BB00D5F144 /* OVR_ThreadCommandQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA4151190722BB00D5F144 /* OVR_ThreadCommandQueue.cpp */; }; + E8AA419A190722BB00D5F144 /* OVR_ThreadCommandQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA4152190722BB00D5F144 /* OVR_ThreadCommandQueue.h */; }; + E8AA41E2190724E600D5F144 /* CAPI_DistortionRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41BA190724E600D5F144 /* CAPI_DistortionRenderer.h */; }; + E8AA41E3190724E600D5F144 /* CAPI_FrameTimeManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41BB190724E600D5F144 /* CAPI_FrameTimeManager.cpp */; }; + E8AA41E4190724E600D5F144 /* CAPI_FrameTimeManager.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41BC190724E600D5F144 /* CAPI_FrameTimeManager.h */; }; + E8AA41E5190724E600D5F144 /* CAPI_GlobalState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41BD190724E600D5F144 /* CAPI_GlobalState.cpp */; }; + E8AA41E6190724E600D5F144 /* CAPI_GlobalState.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41BE190724E600D5F144 /* CAPI_GlobalState.h */; }; + E8AA41E7190724E600D5F144 /* CAPI_HMDRenderState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41BF190724E600D5F144 /* CAPI_HMDRenderState.cpp */; }; + E8AA41E8190724E600D5F144 /* CAPI_HMDRenderState.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41C0190724E600D5F144 /* CAPI_HMDRenderState.h */; }; + E8AA41E9190724E600D5F144 /* CAPI_HMDState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41C1190724E600D5F144 /* CAPI_HMDState.cpp */; }; + E8AA41EA190724E600D5F144 /* CAPI_HMDState.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41C2190724E600D5F144 /* CAPI_HMDState.h */; }; + E8AA41F6190724E600D5F144 /* CAPI_GL_DistortionRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41D0190724E600D5F144 /* CAPI_GL_DistortionRenderer.cpp */; }; + E8AA41F7190724E600D5F144 /* CAPI_GL_DistortionRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41D1190724E600D5F144 /* CAPI_GL_DistortionRenderer.h */; }; + E8AA41F8190724E600D5F144 /* CAPI_GL_Util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8AA41D2190724E600D5F144 /* CAPI_GL_Util.cpp */; }; + E8AA41F9190724E600D5F144 /* CAPI_GL_Util.h in Headers */ = {isa = PBXBuildFile; fileRef = E8AA41D3190724E600D5F144 /* CAPI_GL_Util.h */; }; + E8F1F13E1921911D000EC969 /* OVR_Recording.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E8F1F13D1921911D000EC969 /* OVR_Recording.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E82D4CD31906FE640070CB3F /* libovr.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libovr.a; sourceTree = BUILT_PRODUCTS_DIR; }; + E8754F7F190F1B71005FD401 /* OVR_Recording.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_Recording.h; path = ../../../Src/OVR_Recording.h; sourceTree = ""; }; + E886FE9B190737FA00D5DB45 /* OVR_CAPI_GL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_CAPI_GL.h; path = ../../../Src/OVR_CAPI_GL.h; sourceTree = ""; }; + E886FE9C190737FA00D5DB45 /* OVR_CAPI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_CAPI.cpp; path = ../../../Src/OVR_CAPI.cpp; sourceTree = ""; }; + E886FE9D190737FA00D5DB45 /* OVR_CAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_CAPI.h; path = ../../../Src/OVR_CAPI.h; sourceTree = ""; }; + E886FEA11907528C00D5DB45 /* CAPI_DistortionRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_DistortionRenderer.cpp; sourceTree = ""; }; + E8AA40A61907221900D5F144 /* OVR_Alg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Alg.cpp; sourceTree = ""; }; + E8AA40A71907221900D5F144 /* OVR_Alg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Alg.h; sourceTree = ""; }; + E8AA40A81907221900D5F144 /* OVR_Allocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Allocator.cpp; sourceTree = ""; }; + E8AA40A91907221900D5F144 /* OVR_Allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Allocator.h; sourceTree = ""; }; + E8AA40AA1907221900D5F144 /* OVR_Array.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Array.h; sourceTree = ""; }; + E8AA40AB1907221900D5F144 /* OVR_Atomic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Atomic.cpp; sourceTree = ""; }; + E8AA40AC1907221900D5F144 /* OVR_Atomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Atomic.h; sourceTree = ""; }; + E8AA40AD1907221900D5F144 /* OVR_Color.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Color.h; sourceTree = ""; }; + E8AA40AE1907221900D5F144 /* OVR_ContainerAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_ContainerAllocator.h; sourceTree = ""; }; + E8AA40AF1907221900D5F144 /* OVR_Deque.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Deque.h; sourceTree = ""; }; + E8AA40B01907221900D5F144 /* OVR_File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_File.cpp; sourceTree = ""; }; + E8AA40B11907221900D5F144 /* OVR_File.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_File.h; sourceTree = ""; }; + E8AA40B21907221900D5F144 /* OVR_FileFILE.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_FileFILE.cpp; sourceTree = ""; }; + E8AA40B31907221900D5F144 /* OVR_Hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Hash.h; sourceTree = ""; }; + E8AA40B41907221900D5F144 /* OVR_KeyCodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_KeyCodes.h; sourceTree = ""; }; + E8AA40B51907221900D5F144 /* OVR_List.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_List.h; sourceTree = ""; }; + E8AA40B61907221900D5F144 /* OVR_Lockless.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Lockless.cpp; sourceTree = ""; }; + E8AA40B71907221900D5F144 /* OVR_Lockless.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Lockless.h; sourceTree = ""; }; + E8AA40B81907221900D5F144 /* OVR_Log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Log.cpp; sourceTree = ""; }; + E8AA40B91907221900D5F144 /* OVR_Log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Log.h; sourceTree = ""; }; + E8AA40BA1907221900D5F144 /* OVR_Math.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Math.cpp; sourceTree = ""; }; + E8AA40BB1907221900D5F144 /* OVR_Math.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Math.h; sourceTree = ""; }; + E8AA40BC1907221900D5F144 /* OVR_RefCount.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_RefCount.cpp; sourceTree = ""; }; + E8AA40BD1907221900D5F144 /* OVR_RefCount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_RefCount.h; sourceTree = ""; }; + E8AA40BE1907221900D5F144 /* OVR_Std.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Std.cpp; sourceTree = ""; }; + E8AA40BF1907221900D5F144 /* OVR_Std.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Std.h; sourceTree = ""; }; + E8AA40C01907221900D5F144 /* OVR_String.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String.cpp; sourceTree = ""; }; + E8AA40C11907221900D5F144 /* OVR_String.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_String.h; sourceTree = ""; }; + E8AA40C21907221900D5F144 /* OVR_String_FormatUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String_FormatUtil.cpp; sourceTree = ""; }; + E8AA40C31907221900D5F144 /* OVR_String_PathUtil.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_String_PathUtil.cpp; sourceTree = ""; }; + E8AA40C41907221900D5F144 /* OVR_StringHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_StringHash.h; sourceTree = ""; }; + E8AA40C51907221900D5F144 /* OVR_SysFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_SysFile.cpp; sourceTree = ""; }; + E8AA40C61907221900D5F144 /* OVR_SysFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_SysFile.h; sourceTree = ""; }; + E8AA40C71907221900D5F144 /* OVR_System.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_System.cpp; sourceTree = ""; }; + E8AA40C81907221900D5F144 /* OVR_System.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_System.h; sourceTree = ""; }; + E8AA40C91907221900D5F144 /* OVR_Threads.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Threads.h; sourceTree = ""; }; + E8AA40CA1907221900D5F144 /* OVR_ThreadsPthread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_ThreadsPthread.cpp; sourceTree = ""; }; + E8AA40CC1907221900D5F144 /* OVR_Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_Timer.cpp; sourceTree = ""; }; + E8AA40CD1907221900D5F144 /* OVR_Timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Timer.h; sourceTree = ""; }; + E8AA40CE1907221900D5F144 /* OVR_Types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_Types.h; sourceTree = ""; }; + E8AA40CF1907221900D5F144 /* OVR_UTF8Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OVR_UTF8Util.cpp; sourceTree = ""; }; + E8AA40D01907221900D5F144 /* OVR_UTF8Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OVR_UTF8Util.h; sourceTree = ""; }; + E8AA41011907224700D5F144 /* Util_Interface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Util_Interface.cpp; sourceTree = ""; }; + E8AA41021907224700D5F144 /* Util_Interface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util_Interface.h; sourceTree = ""; }; + E8AA41031907224700D5F144 /* Util_LatencyTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Util_LatencyTest.cpp; sourceTree = ""; }; + E8AA41041907224700D5F144 /* Util_LatencyTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util_LatencyTest.h; sourceTree = ""; }; + E8AA41051907224700D5F144 /* Util_LatencyTest2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Util_LatencyTest2.cpp; sourceTree = ""; }; + E8AA41061907224700D5F144 /* Util_LatencyTest2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util_LatencyTest2.h; sourceTree = ""; }; + E8AA41071907224700D5F144 /* Util_Render_Stereo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Util_Render_Stereo.cpp; sourceTree = ""; }; + E8AA41081907224700D5F144 /* Util_Render_Stereo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Util_Render_Stereo.h; sourceTree = ""; }; + E8AA4121190722BB00D5F144 /* OVR_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_Device.h; path = ../../../Src/OVR_Device.h; sourceTree = ""; }; + E8AA4122190722BB00D5F144 /* OVR_DeviceConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_DeviceConstants.h; path = ../../../Src/OVR_DeviceConstants.h; sourceTree = ""; }; + E8AA4123190722BB00D5F144 /* OVR_DeviceHandle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_DeviceHandle.cpp; path = ../../../Src/OVR_DeviceHandle.cpp; sourceTree = ""; }; + E8AA4124190722BB00D5F144 /* OVR_DeviceHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_DeviceHandle.h; path = ../../../Src/OVR_DeviceHandle.h; sourceTree = ""; }; + E8AA4125190722BB00D5F144 /* OVR_DeviceImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_DeviceImpl.cpp; path = ../../../Src/OVR_DeviceImpl.cpp; sourceTree = ""; }; + E8AA4126190722BB00D5F144 /* OVR_DeviceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_DeviceImpl.h; path = ../../../Src/OVR_DeviceImpl.h; sourceTree = ""; }; + E8AA4127190722BB00D5F144 /* OVR_DeviceMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_DeviceMessages.h; path = ../../../Src/OVR_DeviceMessages.h; sourceTree = ""; }; + E8AA4128190722BB00D5F144 /* OVR_HIDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_HIDDevice.h; path = ../../../Src/OVR_HIDDevice.h; sourceTree = ""; }; + E8AA4129190722BB00D5F144 /* OVR_HIDDeviceBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_HIDDeviceBase.h; path = ../../../Src/OVR_HIDDeviceBase.h; sourceTree = ""; }; + E8AA412A190722BB00D5F144 /* OVR_HIDDeviceImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_HIDDeviceImpl.h; path = ../../../Src/OVR_HIDDeviceImpl.h; sourceTree = ""; }; + E8AA412B190722BB00D5F144 /* OVR_JSON.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_JSON.cpp; path = ../../../Src/OVR_JSON.cpp; sourceTree = ""; }; + E8AA412C190722BB00D5F144 /* OVR_JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_JSON.h; path = ../../../Src/OVR_JSON.h; sourceTree = ""; }; + E8AA412D190722BB00D5F144 /* OVR_LatencyTestImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_LatencyTestImpl.cpp; path = ../../../Src/OVR_LatencyTestImpl.cpp; sourceTree = ""; }; + E8AA412E190722BB00D5F144 /* OVR_LatencyTestImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_LatencyTestImpl.h; path = ../../../Src/OVR_LatencyTestImpl.h; sourceTree = ""; }; + E8AA4136190722BB00D5F144 /* OVR_OSX_DeviceManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_OSX_DeviceManager.cpp; path = ../../../Src/OVR_OSX_DeviceManager.cpp; sourceTree = ""; }; + E8AA4137190722BB00D5F144 /* OVR_OSX_DeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_OSX_DeviceManager.h; path = ../../../Src/OVR_OSX_DeviceManager.h; sourceTree = ""; }; + E8AA4138190722BB00D5F144 /* OVR_OSX_HIDDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_OSX_HIDDevice.cpp; path = ../../../Src/OVR_OSX_HIDDevice.cpp; sourceTree = ""; }; + E8AA4139190722BB00D5F144 /* OVR_OSX_HIDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_OSX_HIDDevice.h; path = ../../../Src/OVR_OSX_HIDDevice.h; sourceTree = ""; }; + E8AA413A190722BB00D5F144 /* OVR_OSX_HMDDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_OSX_HMDDevice.cpp; path = ../../../Src/OVR_OSX_HMDDevice.cpp; sourceTree = ""; }; + E8AA413B190722BB00D5F144 /* OVR_OSX_HMDDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_OSX_HMDDevice.h; path = ../../../Src/OVR_OSX_HMDDevice.h; sourceTree = ""; }; + E8AA413C190722BB00D5F144 /* OVR_OSX_SensorDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_OSX_SensorDevice.cpp; path = ../../../Src/OVR_OSX_SensorDevice.cpp; sourceTree = ""; }; + E8AA413D190722BB00D5F144 /* OVR_Profile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_Profile.cpp; path = ../../../Src/OVR_Profile.cpp; sourceTree = ""; }; + E8AA413E190722BB00D5F144 /* OVR_Profile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_Profile.h; path = ../../../Src/OVR_Profile.h; sourceTree = ""; }; + E8AA413F190722BB00D5F144 /* OVR_Sensor2Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_Sensor2Impl.cpp; path = ../../../Src/OVR_Sensor2Impl.cpp; sourceTree = ""; }; + E8AA4140190722BB00D5F144 /* OVR_Sensor2Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_Sensor2Impl.h; path = ../../../Src/OVR_Sensor2Impl.h; sourceTree = ""; }; + E8AA4141190722BB00D5F144 /* OVR_Sensor2ImplUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_Sensor2ImplUtil.h; path = ../../../Src/OVR_Sensor2ImplUtil.h; sourceTree = ""; }; + E8AA4142190722BB00D5F144 /* OVR_SensorCalibration.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_SensorCalibration.cpp; path = ../../../Src/OVR_SensorCalibration.cpp; sourceTree = ""; }; + E8AA4143190722BB00D5F144 /* OVR_SensorCalibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorCalibration.h; path = ../../../Src/OVR_SensorCalibration.h; sourceTree = ""; }; + E8AA4144190722BB00D5F144 /* OVR_SensorFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_SensorFilter.cpp; path = ../../../Src/OVR_SensorFilter.cpp; sourceTree = ""; }; + E8AA4145190722BB00D5F144 /* OVR_SensorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorFilter.h; path = ../../../Src/OVR_SensorFilter.h; sourceTree = ""; }; + E8AA4146190722BB00D5F144 /* OVR_SensorFusion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_SensorFusion.cpp; path = ../../../Src/OVR_SensorFusion.cpp; sourceTree = ""; }; + E8AA4147190722BB00D5F144 /* OVR_SensorFusion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorFusion.h; path = ../../../Src/OVR_SensorFusion.h; sourceTree = ""; }; + E8AA4148190722BB00D5F144 /* OVR_SensorFusionDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorFusionDebug.h; path = ../../../Src/OVR_SensorFusionDebug.h; sourceTree = ""; }; + E8AA4149190722BB00D5F144 /* OVR_SensorImpl_Common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_SensorImpl_Common.cpp; path = ../../../Src/OVR_SensorImpl_Common.cpp; sourceTree = ""; }; + E8AA414A190722BB00D5F144 /* OVR_SensorImpl_Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorImpl_Common.h; path = ../../../Src/OVR_SensorImpl_Common.h; sourceTree = ""; }; + E8AA414B190722BB00D5F144 /* OVR_SensorImpl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_SensorImpl.cpp; path = ../../../Src/OVR_SensorImpl.cpp; sourceTree = ""; }; + E8AA414C190722BB00D5F144 /* OVR_SensorImpl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorImpl.h; path = ../../../Src/OVR_SensorImpl.h; sourceTree = ""; }; + E8AA414D190722BB00D5F144 /* OVR_SensorTimeFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_SensorTimeFilter.cpp; path = ../../../Src/OVR_SensorTimeFilter.cpp; sourceTree = ""; }; + E8AA414E190722BB00D5F144 /* OVR_SensorTimeFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_SensorTimeFilter.h; path = ../../../Src/OVR_SensorTimeFilter.h; sourceTree = ""; }; + E8AA414F190722BB00D5F144 /* OVR_Stereo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_Stereo.cpp; path = ../../../Src/OVR_Stereo.cpp; sourceTree = ""; }; + E8AA4150190722BB00D5F144 /* OVR_Stereo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_Stereo.h; path = ../../../Src/OVR_Stereo.h; sourceTree = ""; }; + E8AA4151190722BB00D5F144 /* OVR_ThreadCommandQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_ThreadCommandQueue.cpp; path = ../../../Src/OVR_ThreadCommandQueue.cpp; sourceTree = ""; }; + E8AA4152190722BB00D5F144 /* OVR_ThreadCommandQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OVR_ThreadCommandQueue.h; path = ../../../Src/OVR_ThreadCommandQueue.h; sourceTree = ""; }; + E8AA41BA190724E600D5F144 /* CAPI_DistortionRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_DistortionRenderer.h; sourceTree = ""; }; + E8AA41BB190724E600D5F144 /* CAPI_FrameTimeManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_FrameTimeManager.cpp; sourceTree = ""; }; + E8AA41BC190724E600D5F144 /* CAPI_FrameTimeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_FrameTimeManager.h; sourceTree = ""; }; + E8AA41BD190724E600D5F144 /* CAPI_GlobalState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_GlobalState.cpp; sourceTree = ""; }; + E8AA41BE190724E600D5F144 /* CAPI_GlobalState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_GlobalState.h; sourceTree = ""; }; + E8AA41BF190724E600D5F144 /* CAPI_HMDRenderState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_HMDRenderState.cpp; sourceTree = ""; }; + E8AA41C0190724E600D5F144 /* CAPI_HMDRenderState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_HMDRenderState.h; sourceTree = ""; }; + E8AA41C1190724E600D5F144 /* CAPI_HMDState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_HMDState.cpp; sourceTree = ""; }; + E8AA41C2190724E600D5F144 /* CAPI_HMDState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_HMDState.h; sourceTree = ""; }; + E8AA41D0190724E600D5F144 /* CAPI_GL_DistortionRenderer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_GL_DistortionRenderer.cpp; sourceTree = ""; }; + E8AA41D1190724E600D5F144 /* CAPI_GL_DistortionRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_GL_DistortionRenderer.h; sourceTree = ""; }; + E8AA41D2190724E600D5F144 /* CAPI_GL_Util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAPI_GL_Util.cpp; sourceTree = ""; }; + E8AA41D3190724E600D5F144 /* CAPI_GL_Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAPI_GL_Util.h; sourceTree = ""; }; + E8F1F13D1921911D000EC969 /* OVR_Recording.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OVR_Recording.cpp; path = ../../../Src/OVR_Recording.cpp; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E82D4CD01906FE640070CB3F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E82D4CCA1906FE640070CB3F = { + isa = PBXGroup; + children = ( + E8AA41B8190724E600D5F144 /* CAPI */, + E8AA40A51907221900D5F144 /* Kernel */, + E8AA40FC1907224700D5F144 /* Util */, + E886FE9B190737FA00D5DB45 /* OVR_CAPI_GL.h */, + E886FE9C190737FA00D5DB45 /* OVR_CAPI.cpp */, + E886FE9D190737FA00D5DB45 /* OVR_CAPI.h */, + E8AA4121190722BB00D5F144 /* OVR_Device.h */, + E8AA4122190722BB00D5F144 /* OVR_DeviceConstants.h */, + E8AA4123190722BB00D5F144 /* OVR_DeviceHandle.cpp */, + E8AA4124190722BB00D5F144 /* OVR_DeviceHandle.h */, + E8AA4125190722BB00D5F144 /* OVR_DeviceImpl.cpp */, + E8AA4126190722BB00D5F144 /* OVR_DeviceImpl.h */, + E8AA4127190722BB00D5F144 /* OVR_DeviceMessages.h */, + E8AA4128190722BB00D5F144 /* OVR_HIDDevice.h */, + E8AA4129190722BB00D5F144 /* OVR_HIDDeviceBase.h */, + E8AA412A190722BB00D5F144 /* OVR_HIDDeviceImpl.h */, + E8AA412B190722BB00D5F144 /* OVR_JSON.cpp */, + E8AA412C190722BB00D5F144 /* OVR_JSON.h */, + E8AA412D190722BB00D5F144 /* OVR_LatencyTestImpl.cpp */, + E8AA412E190722BB00D5F144 /* OVR_LatencyTestImpl.h */, + E8AA4136190722BB00D5F144 /* OVR_OSX_DeviceManager.cpp */, + E8AA4137190722BB00D5F144 /* OVR_OSX_DeviceManager.h */, + E8AA4138190722BB00D5F144 /* OVR_OSX_HIDDevice.cpp */, + E8AA4139190722BB00D5F144 /* OVR_OSX_HIDDevice.h */, + E8AA413A190722BB00D5F144 /* OVR_OSX_HMDDevice.cpp */, + E8AA413B190722BB00D5F144 /* OVR_OSX_HMDDevice.h */, + E8AA413C190722BB00D5F144 /* OVR_OSX_SensorDevice.cpp */, + E8AA413D190722BB00D5F144 /* OVR_Profile.cpp */, + E8AA413E190722BB00D5F144 /* OVR_Profile.h */, + E8F1F13D1921911D000EC969 /* OVR_Recording.cpp */, + E8754F7F190F1B71005FD401 /* OVR_Recording.h */, + E8AA413F190722BB00D5F144 /* OVR_Sensor2Impl.cpp */, + E8AA4140190722BB00D5F144 /* OVR_Sensor2Impl.h */, + E8AA4141190722BB00D5F144 /* OVR_Sensor2ImplUtil.h */, + E8AA4142190722BB00D5F144 /* OVR_SensorCalibration.cpp */, + E8AA4143190722BB00D5F144 /* OVR_SensorCalibration.h */, + E8AA4144190722BB00D5F144 /* OVR_SensorFilter.cpp */, + E8AA4145190722BB00D5F144 /* OVR_SensorFilter.h */, + E8AA4146190722BB00D5F144 /* OVR_SensorFusion.cpp */, + E8AA4147190722BB00D5F144 /* OVR_SensorFusion.h */, + E8AA4148190722BB00D5F144 /* OVR_SensorFusionDebug.h */, + E8AA4149190722BB00D5F144 /* OVR_SensorImpl_Common.cpp */, + E8AA414A190722BB00D5F144 /* OVR_SensorImpl_Common.h */, + E8AA414B190722BB00D5F144 /* OVR_SensorImpl.cpp */, + E8AA414C190722BB00D5F144 /* OVR_SensorImpl.h */, + E8AA414D190722BB00D5F144 /* OVR_SensorTimeFilter.cpp */, + E8AA414E190722BB00D5F144 /* OVR_SensorTimeFilter.h */, + E8AA414F190722BB00D5F144 /* OVR_Stereo.cpp */, + E8AA4150190722BB00D5F144 /* OVR_Stereo.h */, + E8AA4151190722BB00D5F144 /* OVR_ThreadCommandQueue.cpp */, + E8AA4152190722BB00D5F144 /* OVR_ThreadCommandQueue.h */, + E82D4CD41906FE640070CB3F /* Products */, + ); + sourceTree = ""; + }; + E82D4CD41906FE640070CB3F /* Products */ = { + isa = PBXGroup; + children = ( + E82D4CD31906FE640070CB3F /* libovr.a */, + ); + name = Products; + sourceTree = ""; + }; + E8AA40A51907221900D5F144 /* Kernel */ = { + isa = PBXGroup; + children = ( + E8AA40A61907221900D5F144 /* OVR_Alg.cpp */, + E8AA40A71907221900D5F144 /* OVR_Alg.h */, + E8AA40A81907221900D5F144 /* OVR_Allocator.cpp */, + E8AA40A91907221900D5F144 /* OVR_Allocator.h */, + E8AA40AA1907221900D5F144 /* OVR_Array.h */, + E8AA40AB1907221900D5F144 /* OVR_Atomic.cpp */, + E8AA40AC1907221900D5F144 /* OVR_Atomic.h */, + E8AA40AD1907221900D5F144 /* OVR_Color.h */, + E8AA40AE1907221900D5F144 /* OVR_ContainerAllocator.h */, + E8AA40AF1907221900D5F144 /* OVR_Deque.h */, + E8AA40B01907221900D5F144 /* OVR_File.cpp */, + E8AA40B11907221900D5F144 /* OVR_File.h */, + E8AA40B21907221900D5F144 /* OVR_FileFILE.cpp */, + E8AA40B31907221900D5F144 /* OVR_Hash.h */, + E8AA40B41907221900D5F144 /* OVR_KeyCodes.h */, + E8AA40B51907221900D5F144 /* OVR_List.h */, + E8AA40B61907221900D5F144 /* OVR_Lockless.cpp */, + E8AA40B71907221900D5F144 /* OVR_Lockless.h */, + E8AA40B81907221900D5F144 /* OVR_Log.cpp */, + E8AA40B91907221900D5F144 /* OVR_Log.h */, + E8AA40BA1907221900D5F144 /* OVR_Math.cpp */, + E8AA40BB1907221900D5F144 /* OVR_Math.h */, + E8AA40BC1907221900D5F144 /* OVR_RefCount.cpp */, + E8AA40BD1907221900D5F144 /* OVR_RefCount.h */, + E8AA40BE1907221900D5F144 /* OVR_Std.cpp */, + E8AA40BF1907221900D5F144 /* OVR_Std.h */, + E8AA40C01907221900D5F144 /* OVR_String.cpp */, + E8AA40C11907221900D5F144 /* OVR_String.h */, + E8AA40C21907221900D5F144 /* OVR_String_FormatUtil.cpp */, + E8AA40C31907221900D5F144 /* OVR_String_PathUtil.cpp */, + E8AA40C41907221900D5F144 /* OVR_StringHash.h */, + E8AA40C51907221900D5F144 /* OVR_SysFile.cpp */, + E8AA40C61907221900D5F144 /* OVR_SysFile.h */, + E8AA40C71907221900D5F144 /* OVR_System.cpp */, + E8AA40C81907221900D5F144 /* OVR_System.h */, + E8AA40C91907221900D5F144 /* OVR_Threads.h */, + E8AA40CA1907221900D5F144 /* OVR_ThreadsPthread.cpp */, + E8AA40CC1907221900D5F144 /* OVR_Timer.cpp */, + E8AA40CD1907221900D5F144 /* OVR_Timer.h */, + E8AA40CE1907221900D5F144 /* OVR_Types.h */, + E8AA40CF1907221900D5F144 /* OVR_UTF8Util.cpp */, + E8AA40D01907221900D5F144 /* OVR_UTF8Util.h */, + ); + name = Kernel; + path = ../../../Src/Kernel; + sourceTree = ""; + }; + E8AA40FC1907224700D5F144 /* Util */ = { + isa = PBXGroup; + children = ( + E8AA41011907224700D5F144 /* Util_Interface.cpp */, + E8AA41021907224700D5F144 /* Util_Interface.h */, + E8AA41031907224700D5F144 /* Util_LatencyTest.cpp */, + E8AA41041907224700D5F144 /* Util_LatencyTest.h */, + E8AA41051907224700D5F144 /* Util_LatencyTest2.cpp */, + E8AA41061907224700D5F144 /* Util_LatencyTest2.h */, + E8AA41071907224700D5F144 /* Util_Render_Stereo.cpp */, + E8AA41081907224700D5F144 /* Util_Render_Stereo.h */, + ); + name = Util; + path = ../../../Src/Util; + sourceTree = ""; + }; + E8AA41B8190724E600D5F144 /* CAPI */ = { + isa = PBXGroup; + children = ( + E886FEA11907528C00D5DB45 /* CAPI_DistortionRenderer.cpp */, + E8AA41BA190724E600D5F144 /* CAPI_DistortionRenderer.h */, + E8AA41BB190724E600D5F144 /* CAPI_FrameTimeManager.cpp */, + E8AA41BC190724E600D5F144 /* CAPI_FrameTimeManager.h */, + E8AA41BD190724E600D5F144 /* CAPI_GlobalState.cpp */, + E8AA41BE190724E600D5F144 /* CAPI_GlobalState.h */, + E8AA41BF190724E600D5F144 /* CAPI_HMDRenderState.cpp */, + E8AA41C0190724E600D5F144 /* CAPI_HMDRenderState.h */, + E8AA41C1190724E600D5F144 /* CAPI_HMDState.cpp */, + E8AA41C2190724E600D5F144 /* CAPI_HMDState.h */, + E8AA41CF190724E600D5F144 /* GL */, + ); + name = CAPI; + path = ../../../Src/CAPI; + sourceTree = ""; + }; + E8AA41CF190724E600D5F144 /* GL */ = { + isa = PBXGroup; + children = ( + E8AA41D0190724E600D5F144 /* CAPI_GL_DistortionRenderer.cpp */, + E8AA41D1190724E600D5F144 /* CAPI_GL_DistortionRenderer.h */, + E8AA41D2190724E600D5F144 /* CAPI_GL_Util.cpp */, + E8AA41D3190724E600D5F144 /* CAPI_GL_Util.h */, + ); + path = GL; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + E82D4CD11906FE640070CB3F /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + E8AA4198190722BB00D5F144 /* OVR_Stereo.h in Headers */, + E8AA41E2190724E600D5F144 /* CAPI_DistortionRenderer.h in Headers */, + E8AA41101907224700D5F144 /* Util_LatencyTest.h in Headers */, + E8AA40DF1907221900D5F144 /* OVR_KeyCodes.h in Headers */, + E8AA40D51907221900D5F144 /* OVR_Array.h in Headers */, + E8AA41F7190724E600D5F144 /* CAPI_GL_DistortionRenderer.h in Headers */, + E8AA416F190722BB00D5F144 /* OVR_DeviceMessages.h in Headers */, + E8AA40DC1907221900D5F144 /* OVR_File.h in Headers */, + E8AA40D41907221900D5F144 /* OVR_Allocator.h in Headers */, + E8AA410E1907224700D5F144 /* Util_Interface.h in Headers */, + E8AA418B190722BB00D5F144 /* OVR_SensorCalibration.h in Headers */, + E886FEA0190737FA00D5DB45 /* OVR_CAPI.h in Headers */, + E8AA417F190722BB00D5F144 /* OVR_OSX_DeviceManager.h in Headers */, + E8AA4170190722BB00D5F144 /* OVR_HIDDevice.h in Headers */, + E8AA419A190722BB00D5F144 /* OVR_ThreadCommandQueue.h in Headers */, + E8AA41F9190724E600D5F144 /* CAPI_GL_Util.h in Headers */, + E8AA40E21907221900D5F144 /* OVR_Lockless.h in Headers */, + E8AA40F31907221900D5F144 /* OVR_System.h in Headers */, + E8AA41E8190724E600D5F144 /* CAPI_HMDRenderState.h in Headers */, + E8AA41141907224700D5F144 /* Util_Render_Stereo.h in Headers */, + E8AA40E61907221900D5F144 /* OVR_Math.h in Headers */, + E8AA418D190722BB00D5F144 /* OVR_SensorFilter.h in Headers */, + E8AA40F91907221900D5F144 /* OVR_Types.h in Headers */, + E8AA40D91907221900D5F144 /* OVR_ContainerAllocator.h in Headers */, + E8754F81190F1B71005FD401 /* OVR_Recording.h in Headers */, + E8AA40D71907221900D5F144 /* OVR_Atomic.h in Headers */, + E8AA41E4190724E600D5F144 /* CAPI_FrameTimeManager.h in Headers */, + E8AA41E6190724E600D5F144 /* CAPI_GlobalState.h in Headers */, + E8AA416E190722BB00D5F144 /* OVR_DeviceImpl.h in Headers */, + E8AA40D21907221900D5F144 /* OVR_Alg.h in Headers */, + E8AA40E01907221900D5F144 /* OVR_List.h in Headers */, + E8AA4194190722BB00D5F144 /* OVR_SensorImpl.h in Headers */, + E8AA40EF1907221900D5F144 /* OVR_StringHash.h in Headers */, + E886FE9E190737FA00D5DB45 /* OVR_CAPI_GL.h in Headers */, + E8AA41EA190724E600D5F144 /* CAPI_HMDState.h in Headers */, + E8AA40D81907221900D5F144 /* OVR_Color.h in Headers */, + E8AA4172190722BB00D5F144 /* OVR_HIDDeviceImpl.h in Headers */, + E8AA416C190722BB00D5F144 /* OVR_DeviceHandle.h in Headers */, + E8AA4196190722BB00D5F144 /* OVR_SensorTimeFilter.h in Headers */, + E8AA418F190722BB00D5F144 /* OVR_SensorFusion.h in Headers */, + E8AA4181190722BB00D5F144 /* OVR_OSX_HIDDevice.h in Headers */, + E8AA4174190722BB00D5F144 /* OVR_JSON.h in Headers */, + E8AA4183190722BB00D5F144 /* OVR_OSX_HMDDevice.h in Headers */, + E8AA4171190722BB00D5F144 /* OVR_HIDDeviceBase.h in Headers */, + E8AA4190190722BB00D5F144 /* OVR_SensorFusionDebug.h in Headers */, + E8AA4176190722BB00D5F144 /* OVR_LatencyTestImpl.h in Headers */, + E8AA40F11907221900D5F144 /* OVR_SysFile.h in Headers */, + E8AA40DE1907221900D5F144 /* OVR_Hash.h in Headers */, + E8AA4189190722BB00D5F144 /* OVR_Sensor2ImplUtil.h in Headers */, + E8AA40EC1907221900D5F144 /* OVR_String.h in Headers */, + E8AA4169190722BB00D5F144 /* OVR_Device.h in Headers */, + E8AA4186190722BB00D5F144 /* OVR_Profile.h in Headers */, + E8AA4188190722BB00D5F144 /* OVR_Sensor2Impl.h in Headers */, + E8AA40EA1907221900D5F144 /* OVR_Std.h in Headers */, + E8AA41121907224700D5F144 /* Util_LatencyTest2.h in Headers */, + E8AA40E81907221900D5F144 /* OVR_RefCount.h in Headers */, + E8AA40DA1907221900D5F144 /* OVR_Deque.h in Headers */, + E8AA416A190722BB00D5F144 /* OVR_DeviceConstants.h in Headers */, + E8AA40FB1907221900D5F144 /* OVR_UTF8Util.h in Headers */, + E8AA4192190722BB00D5F144 /* OVR_SensorImpl_Common.h in Headers */, + E8AA40F41907221900D5F144 /* OVR_Threads.h in Headers */, + E8AA40F81907221900D5F144 /* OVR_Timer.h in Headers */, + E8AA40E41907221900D5F144 /* OVR_Log.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + E82D4CD21906FE640070CB3F /* LibOVR */ = { + isa = PBXNativeTarget; + buildConfigurationList = E82D4CD71906FE640070CB3F /* Build configuration list for PBXNativeTarget "LibOVR" */; + buildPhases = ( + E82D4CCF1906FE640070CB3F /* Sources */, + E82D4CD01906FE640070CB3F /* Frameworks */, + E82D4CD11906FE640070CB3F /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LibOVR; + productName = LibOVR; + productReference = E82D4CD31906FE640070CB3F /* libovr.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E82D4CCB1906FE640070CB3F /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0510; + ORGANIZATIONNAME = "Oculus VR Inc."; + }; + buildConfigurationList = E82D4CCE1906FE640070CB3F /* Build configuration list for PBXProject "LibOVR" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = E82D4CCA1906FE640070CB3F; + productRefGroup = E82D4CD41906FE640070CB3F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E82D4CD21906FE640070CB3F /* LibOVR */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + E82D4CCF1906FE640070CB3F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E8AA4175190722BB00D5F144 /* OVR_LatencyTestImpl.cpp in Sources */, + E8AA41E3190724E600D5F144 /* CAPI_FrameTimeManager.cpp in Sources */, + E8AA4199190722BB00D5F144 /* OVR_ThreadCommandQueue.cpp in Sources */, + E8AA417E190722BB00D5F144 /* OVR_OSX_DeviceManager.cpp in Sources */, + E8AA41131907224700D5F144 /* Util_Render_Stereo.cpp in Sources */, + E8AA4182190722BB00D5F144 /* OVR_OSX_HMDDevice.cpp in Sources */, + E8AA4185190722BB00D5F144 /* OVR_Profile.cpp in Sources */, + E8AA40F01907221900D5F144 /* OVR_SysFile.cpp in Sources */, + E8AA40DB1907221900D5F144 /* OVR_File.cpp in Sources */, + E8AA40ED1907221900D5F144 /* OVR_String_FormatUtil.cpp in Sources */, + E8F1F13E1921911D000EC969 /* OVR_Recording.cpp in Sources */, + E8AA41111907224700D5F144 /* Util_LatencyTest2.cpp in Sources */, + E8AA4173190722BB00D5F144 /* OVR_JSON.cpp in Sources */, + E8AA40D61907221900D5F144 /* OVR_Atomic.cpp in Sources */, + E8AA40E31907221900D5F144 /* OVR_Log.cpp in Sources */, + E8AA40E91907221900D5F144 /* OVR_Std.cpp in Sources */, + E8AA4193190722BB00D5F144 /* OVR_SensorImpl.cpp in Sources */, + E8AA41F6190724E600D5F144 /* CAPI_GL_DistortionRenderer.cpp in Sources */, + E8AA40DD1907221900D5F144 /* OVR_FileFILE.cpp in Sources */, + E8AA410F1907224700D5F144 /* Util_LatencyTest.cpp in Sources */, + E8AA40E51907221900D5F144 /* OVR_Math.cpp in Sources */, + E8AA4180190722BB00D5F144 /* OVR_OSX_HIDDevice.cpp in Sources */, + E8AA40D31907221900D5F144 /* OVR_Allocator.cpp in Sources */, + E8AA41E9190724E600D5F144 /* CAPI_HMDState.cpp in Sources */, + E8AA40FA1907221900D5F144 /* OVR_UTF8Util.cpp in Sources */, + E8AA410D1907224700D5F144 /* Util_Interface.cpp in Sources */, + E8AA40F71907221900D5F144 /* OVR_Timer.cpp in Sources */, + E8AA40D11907221900D5F144 /* OVR_Alg.cpp in Sources */, + E8AA4197190722BB00D5F144 /* OVR_Stereo.cpp in Sources */, + E8AA40F21907221900D5F144 /* OVR_System.cpp in Sources */, + E8AA40EB1907221900D5F144 /* OVR_String.cpp in Sources */, + E8AA416B190722BB00D5F144 /* OVR_DeviceHandle.cpp in Sources */, + E8AA418C190722BB00D5F144 /* OVR_SensorFilter.cpp in Sources */, + E886FE9F190737FA00D5DB45 /* OVR_CAPI.cpp in Sources */, + E8AA40E71907221900D5F144 /* OVR_RefCount.cpp in Sources */, + E8AA416D190722BB00D5F144 /* OVR_DeviceImpl.cpp in Sources */, + E8AA41E5190724E600D5F144 /* CAPI_GlobalState.cpp in Sources */, + E8AA418A190722BB00D5F144 /* OVR_SensorCalibration.cpp in Sources */, + E8AA418E190722BB00D5F144 /* OVR_SensorFusion.cpp in Sources */, + E8AA4184190722BB00D5F144 /* OVR_OSX_SensorDevice.cpp in Sources */, + E8AA40E11907221900D5F144 /* OVR_Lockless.cpp in Sources */, + E8AA41E7190724E600D5F144 /* CAPI_HMDRenderState.cpp in Sources */, + E8AA4187190722BB00D5F144 /* OVR_Sensor2Impl.cpp in Sources */, + E8AA41F8190724E600D5F144 /* CAPI_GL_Util.cpp in Sources */, + E8AA4191190722BB00D5F144 /* OVR_SensorImpl_Common.cpp in Sources */, + E8AA40EE1907221900D5F144 /* OVR_String_PathUtil.cpp in Sources */, + E8AA4195190722BB00D5F144 /* OVR_SensorTimeFilter.cpp in Sources */, + E886FEA21907528C00D5DB45 /* CAPI_DistortionRenderer.cpp in Sources */, + E8AA40F51907221900D5F144 /* OVR_ThreadsPthread.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + E82D4CD51906FE640070CB3F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CONFIGURATION_BUILD_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + CONFIGURATION_TEMP_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + OBJROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SYMROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + }; + name = Debug; + }; + E82D4CD61906FE640070CB3F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CONFIGURATION_BUILD_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + CONFIGURATION_TEMP_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + OBJROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + SDKROOT = macosx; + SYMROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + }; + name = Release; + }; + E82D4CD81906FE640070CB3F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CONFIGURATION_BUILD_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + CONFIGURATION_TEMP_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + EXECUTABLE_PREFIX = ""; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + OVR_BUILD_DEBUG, + "$(inherited)", + ); + MACOSX_DEPLOYMENT_TARGET = 10.7; + OBJROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = libovr; + SYMROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + }; + name = Debug; + }; + E82D4CD91906FE640070CB3F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CONFIGURATION_BUILD_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + CONFIGURATION_TEMP_DIR = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + EXECUTABLE_PREFIX = ""; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + OBJROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = libovr; + SYMROOT = "$(SRCROOT)/../../../Lib/Mac/Xcode/$(CONFIGURATION)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E82D4CCE1906FE640070CB3F /* Build configuration list for PBXProject "LibOVR" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E82D4CD51906FE640070CB3F /* Debug */, + E82D4CD61906FE640070CB3F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E82D4CD71906FE640070CB3F /* Build configuration list for PBXNativeTarget "LibOVR" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E82D4CD81906FE640070CB3F /* Debug */, + E82D4CD91906FE640070CB3F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E82D4CCB1906FE640070CB3F /* Project object */; +} diff --git a/modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..6e21728 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Projects/Mac/Xcode/LibOVR.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp new file mode 100644 index 0000000..4b1f5cb --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_DistortionRenderer.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_DistortionRenderer.cpp Content : Combines all of the rendering state associated with the HMD Created : February 2, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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_DistortionRenderer.h" #if defined (OVR_OS_WIN32) // TBD: Move to separate config file that handles back-ends. #define OVR_D3D_VERSION 11 #include "D3D1X/CAPI_D3D1X_DistortionRenderer.h" #undef OVR_D3D_VERSION #define OVR_D3D_VERSION 10 #include "D3D1X/CAPI_D3D1X_DistortionRenderer.h" #undef OVR_D3D_VERSION #define OVR_D3D_VERSION 9 #include "D3D1X/CAPI_D3D9_DistortionRenderer.h" #undef OVR_D3D_VERSION #endif #include "GL/CAPI_GL_DistortionRenderer.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // ***** DistortionRenderer // TBD: Move to separate config file that handles back-ends. DistortionRenderer::CreateFunc DistortionRenderer::APICreateRegistry[ovrRenderAPI_Count] = { 0, // None &GL::DistortionRenderer::Create, 0, // Android_GLES #if defined (OVR_OS_WIN32) &D3D9::DistortionRenderer::Create, &D3D10::DistortionRenderer::Create, &D3D11::DistortionRenderer::Create #else 0, 0, 0 #endif }; }} // namespace OVR::CAPI \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h new file mode 100644 index 0000000..ec0ef17 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_DistortionRenderer.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_DistortionRenderer.h Content : Abstract interface for platform-specific rendering of distortion Created : February 2, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_CAPI_DistortionRenderer_h #define OVR_CAPI_DistortionRenderer_h #include "CAPI_HMDRenderState.h" #include "CAPI_FrameTimeManager.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // ***** CAPI::DistortionRenderer // DistortionRenderer implements rendering of distortion and other overlay elements // in platform-independent way. // Platform-specific renderer back ends for CAPI are derived from this class. class DistortionRenderer : public RefCountBase { // Quiet assignment compiler warning. void operator = (const DistortionRenderer&) { } public: DistortionRenderer(ovrRenderAPIType api, ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState) : RenderAPI(api), HMD(hmd), TimeManager(timeManager), RState(renderState) { } virtual ~DistortionRenderer() { } // Configures the Renderer based on externally passed API settings. Must be // called before use. // Under D3D, apiConfig includes D3D Device pointer, back buffer and other // needed structures. virtual bool Initialize(const ovrRenderAPIConfig* apiConfig, unsigned distortionCaps) = 0; // Submits one eye texture for rendering. This is in the separate method to // allow "submit as you render" scenarios on horizontal screens where one // eye can be scanned out before the other. virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture) = 0; // Finish the frame, optionally swapping buffers. // Many implementations may actually apply the distortion here. virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor) = 0; // Stores the current graphics pipeline state so it can be restored later. void SaveGraphicsState() { if (!(RState.EnabledHmdCaps & ovrHmdCap_NoRestore)) GfxState->Save(); } // Restores the saved graphics pipeline state. void RestoreGraphicsState() { if (!(RState.EnabledHmdCaps & ovrHmdCap_NoRestore)) GfxState->Restore(); } // *** Creation Factory logic ovrRenderAPIType GetRenderAPI() const { return RenderAPI; } // Creation function for this interface, registered for API. typedef DistortionRenderer* (*CreateFunc)(ovrHmd hmd, FrameTimeManager &timeManager, const HMDRenderState& renderState); static CreateFunc APICreateRegistry[ovrRenderAPI_Count]; protected: class GraphicsState : public RefCountBase { public: GraphicsState() : IsValid(false) {} virtual ~GraphicsState() {} virtual void Save() = 0; virtual void Restore() = 0; protected: bool IsValid; }; const ovrRenderAPIType RenderAPI; const ovrHmd HMD; FrameTimeManager& TimeManager; const HMDRenderState& RState; Ptr GfxState; }; }} // namespace OVR::CAPI #endif // OVR_CAPI_DistortionRenderer_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp new file mode 100644 index 0000000..879e906 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_FrameTimeManager.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_FrameTimeManager.cpp Content : Manage frame timing and pose prediction for rendering Created : November 30, 2013 Authors : Volga Aksoy, Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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_FrameTimeManager.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // ***** FrameLatencyTracker FrameLatencyTracker::FrameLatencyTracker() { Reset(); } void FrameLatencyTracker::Reset() { TrackerEnabled = true; WaitMode = SampleWait_Zeroes; FrameIndex = 0; MatchCount = 0; RenderLatencySeconds = 0.0; TimewarpLatencySeconds = 0.0; FrameDeltas.Clear(); } unsigned char FrameLatencyTracker::GetNextDrawColor() { if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes) || (FrameIndex >= FramesTracked)) { return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(0); } OVR_ASSERT(FrameIndex < FramesTracked); return (unsigned char)Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1); } void FrameLatencyTracker::SaveDrawColor(unsigned char drawColor, double endFrameTime, double renderIMUTime, double timewarpIMUTime ) { if (!TrackerEnabled || (WaitMode == SampleWait_Zeroes)) return; if (FrameIndex < FramesTracked) { OVR_ASSERT(Util::FrameTimeRecord::ReadbackIndexToColor(FrameIndex+1) == drawColor); OVR_UNUSED(drawColor); // saves {color, endFrame time} FrameEndTimes[FrameIndex].ReadbackIndex = FrameIndex + 1; FrameEndTimes[FrameIndex].TimeSeconds = endFrameTime; FrameEndTimes[FrameIndex].RenderIMUTimeSeconds = renderIMUTime; FrameEndTimes[FrameIndex].TimewarpIMUTimeSeconds= timewarpIMUTime; FrameEndTimes[FrameIndex].MatchedRecord = false; FrameIndex++; } else { // If the request was outstanding for too long, switch to zero mode to restart. if (endFrameTime > (FrameEndTimes[FrameIndex-1].TimeSeconds + 0.15)) { if (MatchCount == 0) { // If nothing was matched, we have no latency reading. RenderLatencySeconds = 0.0; TimewarpLatencySeconds = 0.0; } WaitMode = SampleWait_Zeroes; MatchCount = 0; FrameIndex = 0; } } } void FrameLatencyTracker::MatchRecord(const Util::FrameTimeRecordSet &r) { if (!TrackerEnabled) return; if (WaitMode == SampleWait_Zeroes) { // Do we have all zeros? if (r.IsAllZeroes()) { OVR_ASSERT(FrameIndex == 0); WaitMode = SampleWait_Match; MatchCount = 0; } return; } // We are in Match Mode. Wait until all colors are matched or timeout, // at which point we go back to zeros. for (int i = 0; i < FrameIndex; i++) { int recordIndex = 0; int consecutiveMatch = 0; OVR_ASSERT(FrameEndTimes[i].ReadbackIndex != 0); if (r.FindReadbackIndex(&recordIndex, FrameEndTimes[i].ReadbackIndex)) { // Advance forward to see that we have several more matches. int ri = recordIndex + 1; int j = i + 1; consecutiveMatch++; for (; (j < FrameIndex) && (ri < Util::FrameTimeRecordSet::RecordCount); j++, ri++) { if (r[ri].ReadbackIndex != FrameEndTimes[j].ReadbackIndex) break; consecutiveMatch++; } // Match at least 2 items in the row, to avoid accidentally matching color. if (consecutiveMatch > 1) { // Record latency values for all but last samples. Keep last 2 samples // for the future to simplify matching. for (int q = 0; q < consecutiveMatch; q++) { const Util::FrameTimeRecord &scanoutFrame = r[recordIndex+q]; FrameTimeRecordEx &renderFrame = FrameEndTimes[i+q]; if (!renderFrame.MatchedRecord) { double deltaSeconds = scanoutFrame.TimeSeconds - renderFrame.TimeSeconds; if (deltaSeconds > 0.0) { FrameDeltas.AddTimeDelta(deltaSeconds); LatencyRecordTime = scanoutFrame.TimeSeconds; RenderLatencySeconds = scanoutFrame.TimeSeconds - renderFrame.RenderIMUTimeSeconds; TimewarpLatencySeconds = (renderFrame.TimewarpIMUTimeSeconds == 0.0) ? 0.0 : (scanoutFrame.TimeSeconds - renderFrame.TimewarpIMUTimeSeconds); } renderFrame.MatchedRecord = true; MatchCount++; } } // Exit for. break; } } } // for ( i => FrameIndex ) // If we matched all frames, start over. if (MatchCount == FramesTracked) { WaitMode = SampleWait_Zeroes; MatchCount = 0; FrameIndex = 0; } } void FrameLatencyTracker::GetLatencyTimings(float latencies[3]) { if (ovr_GetTimeInSeconds() > (LatencyRecordTime + 2.0)) { latencies[0] = 0.0f; latencies[1] = 0.0f; latencies[2] = 0.0f; } else { latencies[0] = (float)RenderLatencySeconds; latencies[1] = (float)TimewarpLatencySeconds; latencies[2] = (float)FrameDeltas.GetMedianTimeDelta(); } } //------------------------------------------------------------------------------------- FrameTimeManager::FrameTimeManager(bool vsyncEnabled) : VsyncEnabled(vsyncEnabled), DynamicPrediction(true), SdkRender(false), FrameTiming() { RenderIMUTimeSeconds = 0.0; TimewarpIMUTimeSeconds = 0.0; // HACK: SyncToScanoutDelay observed close to 1 frame in video cards. // Overwritten by dynamic latency measurement on DK2. VSyncToScanoutDelay = 0.013f; NoVSyncToScanoutDelay = 0.004f; } void FrameTimeManager::Init(HmdRenderInfo& renderInfo) { // Set up prediction distances. // With-Vsync timings. RenderInfo = renderInfo; ScreenSwitchingDelay = RenderInfo.Shutter.PixelSettleTime * 0.5f + RenderInfo.Shutter.PixelPersistence * 0.5f; } void FrameTimeManager::ResetFrameTiming(unsigned frameIndex, bool dynamicPrediction, bool sdkRender) { DynamicPrediction = dynamicPrediction; SdkRender = sdkRender; FrameTimeDeltas.Clear(); DistortionRenderTimes.Clear(); ScreenLatencyTracker.Reset(); FrameTiming.FrameIndex = frameIndex; FrameTiming.NextFrameTime = 0.0; FrameTiming.ThisFrameTime = 0.0; FrameTiming.Inputs.FrameDelta = calcFrameDelta(); FrameTiming.Inputs.ScreenDelay = calcScreenDelay(); FrameTiming.Inputs.TimewarpWaitDelta = 0.0f; LocklessTiming.SetState(FrameTiming); } double FrameTimeManager::calcFrameDelta() const { // Timing difference between frame is tracked by FrameTimeDeltas, or // is a hard-coded value of 1/FrameRate. double frameDelta; if (!VsyncEnabled) { frameDelta = 0.0; } else if (FrameTimeDeltas.GetCount() > 3) { frameDelta = FrameTimeDeltas.GetMedianTimeDelta(); if (frameDelta > (RenderInfo.Shutter.VsyncToNextVsync + 0.001)) frameDelta = RenderInfo.Shutter.VsyncToNextVsync; } else { frameDelta = RenderInfo.Shutter.VsyncToNextVsync; } return frameDelta; } double FrameTimeManager::calcScreenDelay() const { double screenDelay = ScreenSwitchingDelay; double measuredVSyncToScanout; // Use real-time DK2 latency tester HW for prediction if its is working. // Do sanity check under 60 ms if (!VsyncEnabled) { screenDelay += NoVSyncToScanoutDelay; } else if ( DynamicPrediction && (ScreenLatencyTracker.FrameDeltas.GetCount() > 3) && (measuredVSyncToScanout = ScreenLatencyTracker.FrameDeltas.GetMedianTimeDelta(), (measuredVSyncToScanout > 0.0001) && (measuredVSyncToScanout < 0.06)) ) { screenDelay += measuredVSyncToScanout; } else { screenDelay += VSyncToScanoutDelay; } return screenDelay; } double FrameTimeManager::calcTimewarpWaitDelta() const { // If timewarp timing hasn't been calculated, we should wait. if (!VsyncEnabled) return 0.0; if (SdkRender) { if (NeedDistortionTimeMeasurement()) return 0.0; return -(DistortionRenderTimes.GetMedianTimeDelta() + 0.002); } // Just a hard-coded "high" value for game-drawn code. // TBD: Just return 0 and let users calculate this themselves? return -0.003; } void FrameTimeManager::Timing::InitTimingFromInputs(const FrameTimeManager::TimingInputs& inputs, HmdShutterTypeEnum shutterType, double thisFrameTime, unsigned int frameIndex) { // ThisFrameTime comes from the end of last frame, unless it it changed. double nextFrameBase; double frameDelta = inputs.FrameDelta; FrameIndex = frameIndex; ThisFrameTime = thisFrameTime; NextFrameTime = ThisFrameTime + frameDelta; nextFrameBase = NextFrameTime + inputs.ScreenDelay; MidpointTime = nextFrameBase + frameDelta * 0.5; TimewarpPointTime = (inputs.TimewarpWaitDelta == 0.0) ? 0.0 : (NextFrameTime + inputs.TimewarpWaitDelta); // Calculate absolute points in time when eye rendering or corresponding time-warp // screen edges will become visible. // This only matters with VSync. switch(shutterType) { case HmdShutter_RollingTopToBottom: EyeRenderTimes[0] = MidpointTime; EyeRenderTimes[1] = MidpointTime; TimeWarpStartEndTimes[0][0] = nextFrameBase; TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta; TimeWarpStartEndTimes[1][0] = nextFrameBase; TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta; break; case HmdShutter_RollingLeftToRight: EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.25; EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.75; /* // TBD: MA: It is probably better if mesh sets it up per-eye. // Would apply if screen is 0 -> 1 for each eye mesh TimeWarpStartEndTimes[0][0] = nextFrameBase; TimeWarpStartEndTimes[0][1] = MidpointTime; TimeWarpStartEndTimes[1][0] = MidpointTime; TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta; */ // Mesh is set up to vary from Edge of scree 0 -> 1 across both eyes TimeWarpStartEndTimes[0][0] = nextFrameBase; TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta; TimeWarpStartEndTimes[1][0] = nextFrameBase; TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta; break; case HmdShutter_RollingRightToLeft: EyeRenderTimes[0] = nextFrameBase + frameDelta * 0.75; EyeRenderTimes[1] = nextFrameBase + frameDelta * 0.25; // This is *Correct* with Tom's distortion mesh organization. TimeWarpStartEndTimes[0][0] = nextFrameBase ; TimeWarpStartEndTimes[0][1] = nextFrameBase + frameDelta; TimeWarpStartEndTimes[1][0] = nextFrameBase ; TimeWarpStartEndTimes[1][1] = nextFrameBase + frameDelta; break; case HmdShutter_Global: // TBD EyeRenderTimes[0] = MidpointTime; EyeRenderTimes[1] = MidpointTime; TimeWarpStartEndTimes[0][0] = MidpointTime; TimeWarpStartEndTimes[0][1] = MidpointTime; TimeWarpStartEndTimes[1][0] = MidpointTime; TimeWarpStartEndTimes[1][1] = MidpointTime; break; default: break; } } double FrameTimeManager::BeginFrame(unsigned frameIndex) { RenderIMUTimeSeconds = 0.0; TimewarpIMUTimeSeconds = 0.0; // ThisFrameTime comes from the end of last frame, unless it it changed. double thisFrameTime = (FrameTiming.NextFrameTime != 0.0) ? FrameTiming.NextFrameTime : ovr_GetTimeInSeconds(); // We are starting to process a new frame... FrameTiming.InitTimingFromInputs(FrameTiming.Inputs, RenderInfo.Shutter.Type, thisFrameTime, frameIndex); return FrameTiming.ThisFrameTime; } void FrameTimeManager::EndFrame() { // Record timing since last frame; must be called after Present & sync. FrameTiming.NextFrameTime = ovr_GetTimeInSeconds(); if (FrameTiming.ThisFrameTime > 0.0) { FrameTimeDeltas.AddTimeDelta(FrameTiming.NextFrameTime - FrameTiming.ThisFrameTime); FrameTiming.Inputs.FrameDelta = calcFrameDelta(); } // Write to Lock-less LocklessTiming.SetState(FrameTiming); } // Thread-safe function to query timing for a future frame FrameTimeManager::Timing FrameTimeManager::GetFrameTiming(unsigned frameIndex) { Timing frameTiming = LocklessTiming.GetState(); if (frameTiming.ThisFrameTime != 0.0) { // If timing hasn't been initialized, starting based on "now" is the best guess. frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type, ovr_GetTimeInSeconds(), frameIndex); } else if (frameIndex > frameTiming.FrameIndex) { unsigned frameDelta = frameIndex - frameTiming.FrameIndex; double thisFrameTime = frameTiming.NextFrameTime + double(frameDelta-1) * frameTiming.Inputs.FrameDelta; // Don't run away too far into the future beyond rendering. OVR_ASSERT(frameDelta < 6); frameTiming.InitTimingFromInputs(frameTiming.Inputs, RenderInfo.Shutter.Type, thisFrameTime, frameIndex); } return frameTiming; } double FrameTimeManager::GetEyePredictionTime(ovrEyeType eye) { if (VsyncEnabled) { return FrameTiming.EyeRenderTimes[eye]; } // No VSync: Best guess for the near future return ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay; } Transformf FrameTimeManager::GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye) { double eyeRenderTime = GetEyePredictionTime(eye); ovrSensorState eyeState = ovrHmd_GetSensorState(hmd, eyeRenderTime); // EyeRenderPoses[eye] = eyeState.Predicted.Pose; // Record view pose sampling time for Latency reporting. if (RenderIMUTimeSeconds == 0.0) RenderIMUTimeSeconds = eyeState.Recorded.TimeInSeconds; return eyeState.Predicted.Pose; } void FrameTimeManager::GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2]) { if (VsyncEnabled) { timewarpStartEnd[0] = FrameTiming.TimeWarpStartEndTimes[eye][0]; timewarpStartEnd[1] = FrameTiming.TimeWarpStartEndTimes[eye][1]; return; } // Free-running, so this will be displayed immediately. // Unfortunately we have no idea which bit of the screen is actually going to be displayed. // TODO: guess which bit of the screen is being displayed! // (e.g. use DONOTWAIT on present and see when the return isn't WASSTILLWAITING?) // We have no idea where scan-out is currently, so we can't usefully warp the screen spatially. timewarpStartEnd[0] = ovr_GetTimeInSeconds() + ScreenSwitchingDelay + NoVSyncToScanoutDelay; timewarpStartEnd[1] = timewarpStartEnd[0]; } void FrameTimeManager::GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eyeId, ovrPosef renderPose, ovrMatrix4f twmOut[2]) { if (!hmd) { return; } double timewarpStartEnd[2] = { 0.0, 0.0 }; GetTimewarpPredictions(eyeId, timewarpStartEnd); ovrSensorState startState = ovrHmd_GetSensorState(hmd, timewarpStartEnd[0]); ovrSensorState endState = ovrHmd_GetSensorState(hmd, timewarpStartEnd[1]); if (TimewarpIMUTimeSeconds == 0.0) TimewarpIMUTimeSeconds = startState.Recorded.TimeInSeconds; Quatf quatFromStart = startState.Predicted.Pose.Orientation; Quatf quatFromEnd = endState.Predicted.Pose.Orientation; Quatf quatFromEye = renderPose.Orientation; //EyeRenderPoses[eyeId].Orientation; quatFromEye.Invert(); Quatf timewarpStartQuat = quatFromEye * quatFromStart; Quatf timewarpEndQuat = quatFromEye * quatFromEnd; Matrix4f timewarpStart(timewarpStartQuat); Matrix4f timewarpEnd(timewarpEndQuat); // The real-world orientations have: X=right, Y=up, Z=backwards. // The vectors inside the mesh are in NDC to keep the shader simple: X=right, Y=down, Z=forwards. // So we need to perform a similarity transform on this delta matrix. // The verbose code would look like this: /* Matrix4f matBasisChange; matBasisChange.SetIdentity(); matBasisChange.M[0][0] = 1.0f; matBasisChange.M[1][1] = -1.0f; matBasisChange.M[2][2] = -1.0f; Matrix4f matBasisChangeInv = matBasisChange.Inverted(); matRenderFromNow = matBasisChangeInv * matRenderFromNow * matBasisChange; */ // ...but of course all the above is a constant transform and much more easily done. // We flip the signs of the Y&Z row, then flip the signs of the Y&Z column, // and of course most of the flips cancel: // +++ +-- +-- // +++ -> flip Y&Z columns -> +-- -> flip Y&Z rows -> -++ // +++ +-- -++ timewarpStart.M[0][1] = -timewarpStart.M[0][1]; timewarpStart.M[0][2] = -timewarpStart.M[0][2]; timewarpStart.M[1][0] = -timewarpStart.M[1][0]; timewarpStart.M[2][0] = -timewarpStart.M[2][0]; timewarpEnd .M[0][1] = -timewarpEnd .M[0][1]; timewarpEnd .M[0][2] = -timewarpEnd .M[0][2]; timewarpEnd .M[1][0] = -timewarpEnd .M[1][0]; timewarpEnd .M[2][0] = -timewarpEnd .M[2][0]; twmOut[0] = timewarpStart; twmOut[1] = timewarpEnd; } // Used by renderer to determine if it should time distortion rendering. bool FrameTimeManager::NeedDistortionTimeMeasurement() const { if (!VsyncEnabled) return false; return DistortionRenderTimes.GetCount() < 10; } void FrameTimeManager::AddDistortionTimeMeasurement(double distortionTimeSeconds) { DistortionRenderTimes.AddTimeDelta(distortionTimeSeconds); // If timewarp timing changes based on this sample, update it. double newTimewarpWaitDelta = calcTimewarpWaitDelta(); if (newTimewarpWaitDelta != FrameTiming.Inputs.TimewarpWaitDelta) { FrameTiming.Inputs.TimewarpWaitDelta = newTimewarpWaitDelta; LocklessTiming.SetState(FrameTiming); } } void FrameTimeManager::UpdateFrameLatencyTrackingAfterEndFrame( unsigned char frameLatencyTestColor, const Util::FrameTimeRecordSet& rs) { // FrameTiming.NextFrameTime in this context (after EndFrame) is the end frame time. ScreenLatencyTracker.SaveDrawColor(frameLatencyTestColor, FrameTiming.NextFrameTime, RenderIMUTimeSeconds, TimewarpIMUTimeSeconds); ScreenLatencyTracker.MatchRecord(rs); // If screen delay changed, update timing. double newScreenDelay = calcScreenDelay(); if (newScreenDelay != FrameTiming.Inputs.ScreenDelay) { FrameTiming.Inputs.ScreenDelay = newScreenDelay; LocklessTiming.SetState(FrameTiming); } } //----------------------------------------------------------------------------------- // ***** TimeDeltaCollector void TimeDeltaCollector::AddTimeDelta(double timeSeconds) { // avoid adding invalid timing values if(timeSeconds < 0.0f) return; if (Count == Capacity) { for(int i=0; i< Count-1; i++) TimeBufferSeconds[i] = TimeBufferSeconds[i+1]; Count--; } TimeBufferSeconds[Count++] = timeSeconds; } double TimeDeltaCollector::GetMedianTimeDelta() const { double SortedList[Capacity]; bool used[Capacity]; memset(used, 0, sizeof(used)); SortedList[0] = 0.0; // In case Count was 0... // Probably the slowest way to find median... for (int i=0; i= NextFrameTime, since that's the time we expect next // vsync to succeed. double ThisFrameTime; double TimewarpPointTime; double NextFrameTime; double MidpointTime; double EyeRenderTimes[2]; double TimeWarpStartEndTimes[2][2]; Timing() { memset(this, 0, sizeof(Timing)); } void InitTimingFromInputs(const TimingInputs& inputs, HmdShutterTypeEnum shutterType, double thisFrameTime, unsigned int frameIndex); }; // Called on startup to provided data on HMD timing. void Init(HmdRenderInfo& renderInfo); // Called with each new ConfigureRendering. void ResetFrameTiming(unsigned frameIndex, bool dynamicPrediction, bool sdkRender); void SetVsync(bool enabled) { VsyncEnabled = enabled; } // BeginFrame returns time of the call // TBD: Should this be a predicted time value instead ? double BeginFrame(unsigned frameIndex); void EndFrame(); // Thread-safe function to query timing for a future frame Timing GetFrameTiming(unsigned frameIndex); double GetEyePredictionTime(ovrEyeType eye); Transformf GetEyePredictionPose(ovrHmd hmd, ovrEyeType eye); void GetTimewarpPredictions(ovrEyeType eye, double timewarpStartEnd[2]); void GetTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]); // Used by renderer to determine if it should time distortion rendering. bool NeedDistortionTimeMeasurement() const; void AddDistortionTimeMeasurement(double distortionTimeSeconds); // DK2 Lateny test interface // Get next draw color for DK2 latency tester unsigned char GetFrameLatencyTestDrawColor() { return ScreenLatencyTracker.GetNextDrawColor(); } // Must be called after EndFrame() to update latency tester timings. // Must pass color reported by NextFrameColor for this frame. void UpdateFrameLatencyTrackingAfterEndFrame(unsigned char frameLatencyTestColor, const Util::FrameTimeRecordSet& rs); void GetLatencyTimings(float latencies[3]) { return ScreenLatencyTracker.GetLatencyTimings(latencies); } const Timing& GetFrameTiming() const { return FrameTiming; } private: double calcFrameDelta() const; double calcScreenDelay() const; double calcTimewarpWaitDelta() const; HmdRenderInfo RenderInfo; // Timings are collected through a median filter, to avoid outliers. TimeDeltaCollector FrameTimeDeltas; TimeDeltaCollector DistortionRenderTimes; FrameLatencyTracker ScreenLatencyTracker; // Timing changes if we have no Vsync (all prediction is reduced to fixed interval). bool VsyncEnabled; // Set if we are rendering via the SDK, so DistortionRenderTimes is valid. bool DynamicPrediction; // Set if SDk is doing teh rendering. bool SdkRender; // Total frame delay due to VsyncToFirstScanline, persistence and settle time. // Computed from RenderInfor.Shutter. double VSyncToScanoutDelay; double NoVSyncToScanoutDelay; double ScreenSwitchingDelay; // Current (or last) frame timing info. Used as a source for LocklessTiming. Timing FrameTiming; // TBD: Don't we need NextFrame here as well? LocklessUpdater LocklessTiming; // IMU Read timings double RenderIMUTimeSeconds; double TimewarpIMUTimeSeconds; }; }} // namespace OVR::CAPI #endif // OVR_CAPI_FrameTimeManager_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_GlobalState.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_GlobalState.cpp new file mode 100644 index 0000000..f4030c3 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_GlobalState.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_GlobalState.cpp Content : Maintains global state of the CAPI Created : January 24, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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_GlobalState.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // Open Questions / Notes // 2. Detect HMDs. // Challenge: If we do everything through polling, it would imply we want all the devices // initialized. However, there may be multiple rifts, extra sensors, etc, // which shouldn't be allocated. // // How do you reset orientation Quaternion? // Can you change IPD? //------------------------------------------------------------------------------------- // ***** OVRGlobalState // Global instance GlobalState* GlobalState::pInstance = 0; GlobalState::GlobalState() { pManager = *DeviceManager::Create(); // Handle the DeviceManager's messages pManager->AddMessageHandler( this ); EnumerateDevices(); // PhoneSensors::Init(); } GlobalState::~GlobalState() { RemoveHandlerFromDevices(); OVR_ASSERT(HMDs.IsEmpty()); } int GlobalState::EnumerateDevices() { // Need to use separate lock for device enumeration, as pManager->GetHandlerLock() // would produce deadlocks here. Lock::Locker lock(&EnumerationLock); EnumeratedDevices.Clear(); DeviceEnumerator e = pManager->EnumerateDevices(); while(e.IsAvailable()) { EnumeratedDevices.PushBack(DeviceHandle(e)); e.Next(); } return (int)EnumeratedDevices.GetSize(); } HMDDevice* GlobalState::CreateDevice(int index) { Lock::Locker lock(&EnumerationLock); if (index >= (int)EnumeratedDevices.GetSize()) return 0; return EnumeratedDevices[index].CreateDeviceTyped(); } void GlobalState::AddHMD(HMDState* hmd) { Lock::Locker lock(pManager->GetHandlerLock()); HMDs.PushBack(hmd); } void GlobalState::RemoveHMD(HMDState* hmd) { Lock::Locker lock(pManager->GetHandlerLock()); hmd->RemoveNode(); } void GlobalState::NotifyHMDs_AddDevice(DeviceType deviceType) { Lock::Locker lock(pManager->GetHandlerLock()); for(HMDState* hmd = HMDs.GetFirst(); !HMDs.IsNull(hmd); hmd = hmd->pNext) hmd->NotifyAddDevice(deviceType); } void GlobalState::OnMessage(const Message& msg) { if (msg.Type == Message_DeviceAdded || msg.Type == Message_DeviceRemoved) { if (msg.pDevice == pManager) { const MessageDeviceStatus& statusMsg = static_cast(msg); if (msg.Type == Message_DeviceAdded) { //LogText("OnMessage DeviceAdded.\n"); // We may have added a sensor/other device; notify any HMDs that might // need it to check for it later. NotifyHMDs_AddDevice(statusMsg.Handle.GetType()); } else { //LogText("OnMessage DeviceRemoved.\n"); } } } } }} // namespace OVR::CAPI \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_GlobalState.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_GlobalState.h new file mode 100644 index 0000000..cd78170 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_GlobalState.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_GlobalState.h Content : Maintains global state of the CAPI Created : January 24, 2013 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_CAPI_GlobalState_h #define OVR_CAPI_GlobalState_h #include "../OVR_CAPI.h" #include "../OVR_Device.h" #include "../Kernel/OVR_Timer.h" #include "../Kernel/OVR_Math.h" #include "CAPI_HMDState.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // ***** OVRGlobalState // Global DeviceManager state - singleton instance of this is created // by ovr_Initialize(). class GlobalState : public MessageHandler, public NewOverrideBase { public: GlobalState(); ~GlobalState(); static GlobalState *pInstance; int EnumerateDevices(); HMDDevice* CreateDevice(int index); // MessageHandler implementation void OnMessage(const Message& msg); // Helpers used to keep track of HMDs and notify them of sensor changes. void AddHMD(HMDState* hmd); void RemoveHMD(HMDState* hmd); void NotifyHMDs_AddDevice(DeviceType deviceType); const char* GetLastError() { return 0; } DeviceManager* GetManager() { return pManager; } protected: Ptr pManager; Lock EnumerationLock; Array EnumeratedDevices; // Currently created hmds; protected by Manager lock. List HMDs; }; }} // namespace OVR::CAPI #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp new file mode 100644 index 0000000..a7ecae2 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDRenderState.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_CAPI_HMDRenderState.cpp Content : Combines all of the rendering state associated with the HMD Created : February 2, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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_HMDRenderState.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // ***** HMDRenderState HMDRenderState::HMDRenderState(ovrHmd hmd, Profile* userProfile, const OVR::HMDInfo& hmdInfo) : HMD(hmd), HMDInfo(hmdInfo) { RenderInfo = GenerateHmdRenderInfoFromHmdInfo( HMDInfo, userProfile ); Distortion[0] = CalculateDistortionRenderDesc(StereoEye_Left, RenderInfo, 0); Distortion[1] = CalculateDistortionRenderDesc(StereoEye_Right, RenderInfo, 0); ClearColor[0] = ClearColor[1] = ClearColor[2] = ClearColor[3] =0.0f; EnabledHmdCaps = 0; } HMDRenderState::~HMDRenderState() { } ovrHmdDesc HMDRenderState::GetDesc() { ovrHmdDesc d; memset(&d, 0, sizeof(d)); d.Type = ovrHmd_Other; d.ProductName = HMDInfo.ProductName; d.Manufacturer = HMDInfo.Manufacturer; d.Resolution.w = HMDInfo.ResolutionInPixels.w; d.Resolution.h = HMDInfo.ResolutionInPixels.h; d.WindowsPos.x = HMDInfo.DesktopX; d.WindowsPos.y = HMDInfo.DesktopY; d.DisplayDeviceName = HMDInfo.DisplayDeviceName; d.DisplayId = HMDInfo.DisplayId; d.HmdCaps = ovrHmdCap_Present | ovrHmdCap_NoVSync; d.SensorCaps = ovrSensorCap_YawCorrection | ovrSensorCap_Orientation; d.DistortionCaps = ovrDistortionCap_Chromatic | ovrDistortionCap_TimeWarp | ovrDistortionCap_Vignette; if (strstr(HMDInfo.ProductName, "DK1")) { d.Type = ovrHmd_DK1; } else if (strstr(HMDInfo.ProductName, "DK2")) { d.Type = ovrHmd_DK2; d.HmdCaps |= ovrHmdCap_LowPersistence | ovrHmdCap_LatencyTest | ovrHmdCap_DynamicPrediction; d.SensorCaps |= ovrSensorCap_Position; } DistortionRenderDesc& leftDistortion = Distortion[0]; DistortionRenderDesc& rightDistortion = Distortion[1]; // The suggested FOV (assuming eye rotation) d.DefaultEyeFov[0] = CalculateFovFromHmdInfo(StereoEye_Left, leftDistortion, RenderInfo, OVR_DEFAULT_EXTRA_EYE_ROTATION); d.DefaultEyeFov[1] = CalculateFovFromHmdInfo(StereoEye_Right, rightDistortion, RenderInfo, OVR_DEFAULT_EXTRA_EYE_ROTATION); // FOV extended across the entire screen d.MaxEyeFov[0] = GetPhysicalScreenFov(StereoEye_Left, leftDistortion); d.MaxEyeFov[1] = GetPhysicalScreenFov(StereoEye_Right, rightDistortion); if (HMDInfo.Shutter.Type == HmdShutter_RollingRightToLeft) { d.EyeRenderOrder[0] = ovrEye_Right; d.EyeRenderOrder[1] = ovrEye_Left; } else { d.EyeRenderOrder[0] = ovrEye_Left; d.EyeRenderOrder[1] = ovrEye_Right; } return d; } ovrSizei HMDRenderState::GetFOVTextureSize(int eye, ovrFovPort fov, float pixelsPerDisplayPixel) { OVR_ASSERT((unsigned)eye < 2); StereoEye seye = (eye == ovrEye_Left) ? StereoEye_Left : StereoEye_Right; return CalculateIdealPixelSize(seye, Distortion[eye], fov, pixelsPerDisplayPixel); } ovrEyeRenderDesc HMDRenderState::calcRenderDesc(ovrEyeType eyeType, const ovrFovPort& fov) { HmdRenderInfo& hmdri = RenderInfo; StereoEye eye = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right; ovrEyeRenderDesc e0; e0.Eye = eyeType; e0.Fov = fov; e0.ViewAdjust = CalculateEyeVirtualCameraOffset(hmdri, eye, false); e0.DistortedViewport = GetFramebufferViewport(eye, hmdri); e0.PixelsPerTanAngleAtCenter = Distortion[0].PixelsPerTanAngleAtCenter; return e0; } void HMDRenderState::setupRenderDesc( ovrEyeRenderDesc eyeRenderDescOut[2], const ovrFovPort eyeFovIn[2] ) { eyeRenderDescOut[0] = EyeRenderDesc[0] = calcRenderDesc(ovrEye_Left, eyeFovIn[0]); eyeRenderDescOut[1] = EyeRenderDesc[1] = calcRenderDesc(ovrEye_Right, eyeFovIn[1]); } }} // namespace OVR::CAPI \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDRenderState.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDRenderState.h new file mode 100644 index 0000000..d3eb3f5 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDRenderState.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_HMDRenderState.h Content : Combines all of the rendering state associated with the HMD Created : February 2, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_CAPI_HMDRenderState_h #define OVR_CAPI_HMDRenderState_h #include "../OVR_CAPI.h" #include "../Kernel/OVR_Math.h" #include "../Util/Util_Render_Stereo.h" namespace OVR { namespace CAPI { using namespace OVR::Util::Render; //------------------------------------------------------------------------------------- // ***** HMDRenderState // Combines all of the rendering setup information about one HMD. class HMDRenderState : public NewOverrideBase { // Quiet assignment compiler warning. void operator = (const HMDRenderState&) { } public: HMDRenderState(ovrHmd hmd, Profile* userProfile, const OVR::HMDInfo& hmdInfo); virtual ~HMDRenderState(); // *** Rendering Setup // Delegated access APIs ovrHmdDesc GetDesc(); ovrSizei GetFOVTextureSize(int eye, ovrFovPort fov, float pixelsPerDisplayPixel); ovrEyeRenderDesc calcRenderDesc(ovrEyeType eyeType, const ovrFovPort& fov); void setupRenderDesc(ovrEyeRenderDesc eyeRenderDescOut[2], const ovrFovPort eyeFovIn[2]); public: // HMDInfo shouldn't change, as its string pointers are passed out. ovrHmd HMD; const OVR::HMDInfo& HMDInfo; //const char* pLastError; HmdRenderInfo RenderInfo; DistortionRenderDesc Distortion[2]; ovrEyeRenderDesc EyeRenderDesc[2]; // Clear color used for distortion float ClearColor[4]; // Pose at which last time the eye was rendered, as submitted by EndEyeRender. ovrPosef EyeRenderPoses[2]; // Capabilities passed to Configure. unsigned EnabledHmdCaps; unsigned DistortionCaps; }; }} // namespace OVR::CAPI #endif // OVR_CAPI_HMDState_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDState.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDState.cpp new file mode 100644 index 0000000..413d57f --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDState.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_HMDState.cpp Content : State associated with a single HMD Created : January 24, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "CAPI_GlobalState.h" #include "../OVR_Profile.h" namespace OVR { namespace CAPI { //------------------------------------------------------------------------------------- // ***** HMDState HMDState::HMDState(HMDDevice* device) : pHMD(device), HMDInfoW(device), HMDInfo(HMDInfoW.h), EnabledHmdCaps(0), HmdCapsAppliedToSensor(0), SensorStarted(0), SensorCreated(0), SensorCaps(0), AddSensorCount(0), AddLatencyTestCount(0), AddLatencyTestDisplayCount(0), RenderState(getThis(), pHMD->GetProfile(), HMDInfoW.h), LastFrameTimeSeconds(0.0f), LastGetFrameTimeSeconds(0.0), LatencyTestActive(false), LatencyTest2Active(false) { pLastError = 0; GlobalState::pInstance->AddHMD(this); // Should be in renderer? TimeManager.Init(RenderState.RenderInfo); EyeRenderActive[0] = false; EyeRenderActive[1] = false; LatencyTestDrawColor[0] = 0; LatencyTestDrawColor[1] = 0; LatencyTestDrawColor[2] = 0; OVR_CAPI_VISION_CODE( pPoseTracker = 0; ) RenderingConfigured = false; BeginFrameCalled = false; BeginFrameThreadId = 0; BeginFrameTimingCalled = false; } HMDState::HMDState(ovrHmdType hmdType) : pHMD(0), HMDInfoW(hmdType), HMDInfo(HMDInfoW.h), EnabledHmdCaps(0), SensorStarted(0), SensorCreated(0), SensorCaps(0), AddSensorCount(0), AddLatencyTestCount(0), AddLatencyTestDisplayCount(0), RenderState(getThis(), 0, HMDInfoW.h), // No profile. LastFrameTimeSeconds(0.0), LastGetFrameTimeSeconds(0.0) { // TBD: We should probably be looking up the default profile for the given // device type + user. pLastError = 0; GlobalState::pInstance->AddHMD(this); // Should be in renderer? TimeManager.Init(RenderState.RenderInfo); EyeRenderActive[0] = false; EyeRenderActive[1] = false; OVR_CAPI_VISION_CODE( pPoseTracker = 0; ) RenderingConfigured = false; BeginFrameCalled = false; BeginFrameThreadId = 0; BeginFrameTimingCalled = false; } HMDState::~HMDState() { OVR_ASSERT(GlobalState::pInstance); StopSensor(); ConfigureRendering(0,0,0,0); OVR_CAPI_VISION_CODE( OVR_ASSERT(pPoseTracker == 0); ) GlobalState::pInstance->RemoveHMD(this); } //------------------------------------------------------------------------------------- // *** Sensor bool HMDState::StartSensor(unsigned supportedCaps, unsigned requiredCaps) { Lock::Locker lockScope(&DevicesLock); bool crystalCoveOrBetter = (HMDInfo.HmdType == HmdType_CrystalCoveProto) || (HMDInfo.HmdType == HmdType_DK2); bool sensorCreatedJustNow = false; // TBD: In case of sensor not being immediately available, it would be good to check // yaw config availability to match it with ovrHmdCap_YawCorrection requirement. // if (!crystalCoveOrBetter) { if (requiredCaps & ovrSensorCap_Position) { pLastError = "ovrSensorCap_Position not supported on this HMD."; return false; } } supportedCaps |= requiredCaps; if (pHMD && !pSensor) { // Zero AddSensorCount before creation, in case it fails (or succeeds but then // immediately gets disconnected) followed by another Add notification. AddSensorCount = 0; pSensor = *pHMD->GetSensor(); sensorCreatedJustNow= true; if (pSensor) { pSensor->SetReportRate(500); SFusion.AttachToSensor(pSensor); applyProfileToSensorFusion(); } else { if (requiredCaps & ovrSensorCap_Orientation) { pLastError = "Failed to create sensor."; return false; } } } if ((requiredCaps & ovrSensorCap_YawCorrection) && !pSensor->IsMagCalibrated()) { pLastError = "ovrHmdCap_YawCorrection not available."; if (sensorCreatedJustNow) { SFusion.AttachToSensor(0); SFusion.Reset(); pSensor.Clear(); } return false; } SFusion.SetYawCorrectionEnabled((supportedCaps & ovrSensorCap_YawCorrection) != 0); if (pSensor && sensorCreatedJustNow) { LogText("Sensor created.\n"); SensorCreated = true; } updateDK2FeaturesTiedToSensor(sensorCreatedJustNow); #ifdef OVR_CAPI_VISIONSUPPORT if (crystalCoveOrBetter && (supportedCaps & ovrSensorCap_Position)) { if (!pPoseTracker) { pPoseTracker = new Vision::PoseTracker(SFusion); if (pPoseTracker) { pPoseTracker->AssociateHMD(pSensor); LogText("Sensor Pose tracker created.\n"); } } // TBD: How do we verify that position tracking is actually available // i.e. camera is plugged in? } else if (pPoseTracker) { // TBD: Internals not thread safe - must fix!! delete pPoseTracker; pPoseTracker = 0; LogText("Sensor Pose tracker destroyed.\n"); } #endif // OVR_CAPI_VISIONSUPPORT SensorCaps = supportedCaps; SensorStarted = true; return true; } // Stops sensor sampling, shutting down internal resources. void HMDState::StopSensor() { Lock::Locker lockScope(&DevicesLock); if (SensorStarted) { #ifdef OVR_CAPI_VISIONSUPPORT if (pPoseTracker) { // TBD: Internals not thread safe - must fix!! delete pPoseTracker; pPoseTracker = 0; LogText("Sensor Pose tracker destroyed.\n"); } #endif // OVR_CAPI_VISION_CODE SFusion.AttachToSensor(0); SFusion.Reset(); pSensor.Clear(); HmdCapsAppliedToSensor = 0; AddSensorCount = 0; SensorCaps = 0; SensorCreated = false; SensorStarted = false; LogText("StopSensor succeeded.\n"); } } // Resets sensor orientation. void HMDState::ResetSensor() { SFusion.Reset(); } // Returns prediction for time. ovrSensorState HMDState::PredictedSensorState(double absTime) { SensorState ss; // We are trying to keep this path lockless unless we are notified of new device // creation while not having a sensor yet. It's ok to check SensorCreated volatile // flag here, since GetSensorStateAtTime() is internally lockless and safe. if (SensorCreated || checkCreateSensor()) { ss = SFusion.GetSensorStateAtTime(absTime); if (!(ss.StatusFlags & ovrStatus_OrientationTracked)) { Lock::Locker lockScope(&DevicesLock); #ifdef OVR_CAPI_VISIONSUPPORT if (pPoseTracker) { // TBD: Internals not thread safe - must fix!! delete pPoseTracker; pPoseTracker = 0; LogText("Sensor Pose tracker destroyed.\n"); } #endif // OVR_CAPI_VISION_CODE // Not needed yet; SFusion.AttachToSensor(0); // This seems to reset orientation anyway... pSensor.Clear(); SensorCreated = false; HmdCapsAppliedToSensor = 0; } } else { // SensorState() defaults to 0s. // ss.Pose.Orientation = Quatf(); // .. // John: // We still want valid times so frames will get a delta-time // and allow operation with a joypad when the sensor isn't // connected. ss.Recorded.TimeInSeconds = absTime; ss.Predicted.TimeInSeconds = absTime; } ss.StatusFlags |= ovrStatus_HmdConnected; return ss; } bool HMDState::checkCreateSensor() { if (!(SensorStarted && !SensorCreated && AddSensorCount)) return false; Lock::Locker lockScope(&DevicesLock); // Re-check condition once in the lock, in case the state changed. if (SensorStarted && !SensorCreated && AddSensorCount) { if (pHMD) { AddSensorCount = 0; pSensor = *pHMD->GetSensor(); } if (pSensor) { pSensor->SetReportRate(500); SFusion.AttachToSensor(pSensor); SFusion.SetYawCorrectionEnabled((SensorCaps & ovrSensorCap_YawCorrection) != 0); applyProfileToSensorFusion(); #ifdef OVR_CAPI_VISIONSUPPORT if (SensorCaps & ovrSensorCap_Position) { pPoseTracker = new Vision::PoseTracker(SFusion); if (pPoseTracker) { pPoseTracker->AssociateHMD(pSensor); } LogText("Sensor Pose tracker created.\n"); } #endif // OVR_CAPI_VISION_CODE LogText("Sensor created.\n"); SensorCreated = true; return true; } } return SensorCreated; } bool HMDState::GetSensorDesc(ovrSensorDesc* descOut) { Lock::Locker lockScope(&DevicesLock); if (SensorCreated) { OVR_ASSERT(pSensor); OVR::SensorInfo si; pSensor->GetDeviceInfo(&si); descOut->VendorId = si.VendorId; descOut->ProductId = si.ProductId; OVR_ASSERT(si.SerialNumber.GetSize() <= sizeof(descOut->SerialNumber)); OVR_strcpy(descOut->SerialNumber, sizeof(descOut->SerialNumber), si.SerialNumber.ToCStr()); return true; } return false; } void HMDState::applyProfileToSensorFusion() { if (!pHMD) return; Profile* profile = pHMD->GetProfile(); if (!profile) { OVR_ASSERT(false); return; } SFusion.SetUserHeadDimensions ( *profile, RenderState.RenderInfo ); } void HMDState::updateLowPersistenceMode(bool lowPersistence) const { OVR_ASSERT(pSensor); DisplayReport dr; if (pSensor.GetPtr()) { pSensor->GetDisplayReport(&dr); dr.Persistence = (UInt16) (dr.TotalRows * (lowPersistence ? 0.18f : 1.0f)); dr.Brightness = lowPersistence ? 255 : 0; pSensor->SetDisplayReport(dr); } } void HMDState::updateLatencyTestForHmd(bool latencyTesting) { if (pSensor.GetPtr()) { DisplayReport dr; pSensor->GetDisplayReport(&dr); dr.ReadPixel = latencyTesting; pSensor->SetDisplayReport(dr); } if (latencyTesting) { LatencyUtil2.SetSensorDevice(pSensor.GetPtr()); } else { LatencyUtil2.SetSensorDevice(NULL); } } void HMDState::updateDK2FeaturesTiedToSensor(bool sensorCreatedJustNow) { Lock::Locker lockScope(&DevicesLock); if (!SensorCreated || (HMDInfo.HmdType != HmdType_DK2)) return; // Only send display reports if state changed or sensor initializing first time. if (sensorCreatedJustNow || ((HmdCapsAppliedToSensor ^ EnabledHmdCaps) & ovrHmdCap_LowPersistence)) { updateLowPersistenceMode((EnabledHmdCaps & ovrHmdCap_LowPersistence) ? true : false); } if (sensorCreatedJustNow || ((HmdCapsAppliedToSensor ^ EnabledHmdCaps) & ovrHmdCap_LatencyTest)) { updateLatencyTestForHmd((EnabledHmdCaps & ovrHmdCap_LatencyTest) != 0); } HmdCapsAppliedToSensor = EnabledHmdCaps & (ovrHmdCap_LowPersistence|ovrHmdCap_LatencyTest); } void HMDState::SetEnabledHmdCaps(unsigned hmdCaps) { if (HMDInfo.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); } EnabledHmdCaps = hmdCaps & ovrHmdCap_Writable_Mask; RenderState.EnabledHmdCaps = EnabledHmdCaps; // Unfortunately, LowPersistance and other flags are tied to sensor. // This flag will apply the state of sensor is created; otherwise this will be delayed // till StartSensor. // Such behavior is less then ideal, but should be resolved with the service model. updateDK2FeaturesTiedToSensor(false); } //------------------------------------------------------------------------------------- // ***** Property Access // TBD: This all needs to be cleaned up and organized into namespaces. float HMDState::getFloatValue(const char* propertyName, float defaultVal) { if (OVR_strcmp(propertyName, "LensSeparation") == 0) { return HMDInfo.LensSeparationInMeters; } else if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0) { return SFusion.GetCenterPupilDepth(); } else if (pHMD) { Profile* p = pHMD->GetProfile(); if (p) { return p->GetFloatValue(propertyName, defaultVal); } } return defaultVal; } bool HMDState::setFloatValue(const char* propertyName, float value) { if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0) { SFusion.SetCenterPupilDepth(value); return true; } 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] = { HMDInfo.ScreenSizeInMeters.w, HMDInfo.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 (HMDInfo.HmdType != HmdType_DK2) return 0; float data[3]; TimeManager.GetLatencyTimings(data); return CopyFloatArrayWithLimit(values, arraySize, data, 3); } /* else if (OVR_strcmp(propertyName, "CenterPupilDepth") == 0) { if (arraySize >= 1) { values[0] = SFusion.GetCenterPupilDepth(); return 1; } return 0; } */ else if (pHMD) { Profile* p = pHMD->GetProfile(); // TBD: Not quite right. Should update profile interface, so that // we can return 0 in all conditions if property doesn't exist. if (p) { unsigned count = p->GetFloatValues(propertyName, values, arraySize); return count; } } } 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; } return false; } const char* HMDState::getString(const char* propertyName, const char* defaultVal) { if (pHMD) { // For now, just access the profile. Profile* p = pHMD->GetProfile(); LastGetStringValue[0] = 0; if (p && p->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue))) { return LastGetStringValue; } } return defaultVal; } //------------------------------------------------------------------------------------- // *** Latency Test bool HMDState::ProcessLatencyTest(unsigned char rgbColorOut[3]) { bool result = false; // Check create. if (pLatencyTester) { if (pLatencyTester->IsConnected()) { Color colorToDisplay; LatencyUtil.ProcessInputs(); result = LatencyUtil.DisplayScreenColor(colorToDisplay); rgbColorOut[0] = colorToDisplay.R; rgbColorOut[1] = colorToDisplay.G; rgbColorOut[2] = colorToDisplay.B; } else { // Disconnect. LatencyUtil.SetDevice(NULL); pLatencyTester = 0; LogText("LATENCY SENSOR disconnected.\n"); } } else if (AddLatencyTestCount > 0) { // This might have some unlikely race condition issue which could cause us to miss a device... AddLatencyTestCount = 0; pLatencyTester = *GlobalState::pInstance->GetManager()-> EnumerateDevices().CreateDevice(); if (pLatencyTester) { LatencyUtil.SetDevice(pLatencyTester); LogText("LATENCY TESTER connected\n"); } } return result; } void HMDState::ProcessLatencyTest2(unsigned char rgbColorOut[3], double startTime) { // Check create. if (!(EnabledHmdCaps & ovrHmdCap_LatencyTest)) return; if (pLatencyTesterDisplay && !LatencyUtil2.HasDisplayDevice()) { if (!pLatencyTesterDisplay->IsConnected()) { LatencyUtil2.SetDisplayDevice(NULL); } } else if (AddLatencyTestDisplayCount > 0) { // This might have some unlikely race condition issue // which could cause us to miss a device... AddLatencyTestDisplayCount = 0; pLatencyTesterDisplay = *GlobalState::pInstance->GetManager()-> EnumerateDevices().CreateDevice(); if (pLatencyTesterDisplay) { LatencyUtil2.SetDisplayDevice(pLatencyTesterDisplay); } } if (LatencyUtil2.HasDevice() && pSensor && pSensor->IsConnected()) { LatencyUtil2.BeginTest(startTime); Color colorToDisplay; LatencyTest2Active = LatencyUtil2.DisplayScreenColor(colorToDisplay); rgbColorOut[0] = colorToDisplay.R; rgbColorOut[1] = colorToDisplay.G; rgbColorOut[2] = colorToDisplay.B; } else { LatencyTest2Active = false; } } //------------------------------------------------------------------------------------- // *** 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 (pRenderer) pRenderer.Clear(); RenderingConfigured = false; return true; } if (pRenderer && (apiConfig->Header.API != pRenderer->GetRenderAPI())) { // Shutdown old renderer. if (pRenderer) pRenderer.Clear(); } // Step 1: do basic setup configuration RenderState.setupRenderDesc(eyeRenderDescOut, eyeFovIn); RenderState.EnabledHmdCaps = EnabledHmdCaps; // This is a copy... Any cleaner way? RenderState.DistortionCaps = distortionCaps; 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](this, TimeManager, RenderState); } if (!pRenderer || !pRenderer->Initialize(apiConfig, distortionCaps)) { RenderingConfigured = false; return false; } return true; } ovrPosef HMDState::BeginEyeRender(ovrEyeType eye) { // Debug checks. checkBeginFrameScope("ovrHmd_BeginEyeRender"); ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_BeginEyeRender"); // Unknown eyeId provided in ovrHmd_BeginEyeRender OVR_ASSERT_LOG(eye == ovrEye_Left || eye == ovrEye_Right, ("ovrHmd_BeginEyeRender eyeId out of range.")); OVR_ASSERT_LOG(EyeRenderActive[eye] == false, ("Multiple calls to ovrHmd_BeginEyeRender for the same eye.")); EyeRenderActive[eye] = true; // Only process latency tester for drawing the left eye (assumes left eye is drawn first) if (pRenderer && eye == 0) { LatencyTestActive = ProcessLatencyTest(LatencyTestDrawColor); } return ovrHmd_GetEyePose(this, eye); } void HMDState::EndEyeRender(ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture) { // Debug checks. checkBeginFrameScope("ovrHmd_EndEyeRender"); ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_EndEyeRender"); if (!EyeRenderActive[eye]) { OVR_ASSERT_LOG(false, ("ovrHmd_EndEyeRender called without ovrHmd_BeginEyeRender.")); return; } RenderState.EyeRenderPoses[eye] = renderPose; if (pRenderer) pRenderer->SubmitEye(eye, eyeTexture); EyeRenderActive[eye] = false; } }} // namespace OVR::CAPI \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDState.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDState.h new file mode 100644 index 0000000..8737b24 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/CAPI_HMDState.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_HMDState.h Content : State associated with a single HMD Created : January 24, 2014 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_CAPI_HMDState_h #define OVR_CAPI_HMDState_h #include "../Kernel/OVR_Math.h" #include "../Kernel/OVR_List.h" #include "../Kernel/OVR_Log.h" #include "../OVR_CAPI.h" #include "../OVR_SensorFusion.h" #include "../Util/Util_LatencyTest.h" #include "../Util/Util_LatencyTest2.h" #include "CAPI_FrameTimeManager.h" #include "CAPI_HMDRenderState.h" #include "CAPI_DistortionRenderer.h" // Define OVR_CAPI_VISIONSUPPORT to compile in vision support #ifdef OVR_CAPI_VISIONSUPPORT #define OVR_CAPI_VISION_CODE(c) c #include "../Vision/Vision_PoseTracker.h" #else #define OVR_CAPI_VISION_CODE(c) #endif struct ovrHmdStruct { }; namespace OVR { namespace CAPI { using namespace OVR::Util::Render; //------------------------------------------------------------------------------------- // ***** ThreadChecker // This helper class is used to verify that the API is used according to supported // thread safety constraints (is not re-entrant for this and related functions). class ThreadChecker { public: #ifndef OVR_BUILD_DEBUG // In release build, thread checks are disabled. ThreadChecker() { } void Begin(const char* functionName) { OVR_UNUSED1(functionName); } void End() { } // Add thread-re-entrancy check for function scope struct Scope { Scope(ThreadChecker*, const char *) { } ~Scope() { } }; #else // OVR_BUILD_DEBUG ThreadChecker() : pFunctionName(0), FirstThread(0) { } void Begin(const char* functionName) { if (!pFunctionName) { pFunctionName = functionName; FirstThread = GetCurrentThreadId(); } else { // pFunctionName may be not null here if function is called internally on the same thread. OVR_ASSERT_LOG((FirstThread == GetCurrentThreadId()), ("%s (threadId=%p) called at the same times as %s (threadId=%p)\n", functionName, GetCurrentThreadId(), pFunctionName, FirstThread) ); } } void End() { pFunctionName = 0; FirstThread = 0; } // Add thread-re-entrancy check for function scope. struct Scope { Scope(ThreadChecker* threadChecker, const char *functionName) : pChecker(threadChecker) { pChecker->Begin(functionName); } ~Scope() { pChecker->End(); } private: ThreadChecker* pChecker; }; private: // If not 0, contains the name of the function that first entered the scope. const char * pFunctionName; ThreadId FirstThread; #endif // OVR_BUILD_DEBUG }; //------------------------------------------------------------------------------------- // ***** HMDState // Describes a single HMD. class HMDState : public ListNode, public ovrHmdStruct, public NewOverrideBase { public: HMDState(HMDDevice* device); HMDState(ovrHmdType hmdType); virtual ~HMDState(); // *** Sensor Setup bool StartSensor(unsigned supportedCaps, unsigned requiredCaps); void StopSensor(); void ResetSensor(); ovrSensorState PredictedSensorState(double absTime); bool GetSensorDesc(ovrSensorDesc* descOut); // Changes HMD Caps. // Capability bits that are not directly or logically tied to one system (such as sensor) // are grouped here. ovrHmdCap_VSync, for example, affects rendering and timing. void SetEnabledHmdCaps(unsigned caps); bool ProcessLatencyTest(unsigned char rgbColorOut[3]); void ProcessLatencyTest2(unsigned char rgbColorOut[3], double startTime); // *** Rendering Setup bool ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2], const ovrFovPort eyeFovIn[2], const ovrRenderAPIConfig* apiConfig, unsigned distortionCaps); ovrPosef BeginEyeRender(ovrEyeType eye); void EndEyeRender(ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture); const char* GetLastError() { const char* p = pLastError; pLastError = 0; return p; } void NotifyAddDevice(DeviceType deviceType) { if (deviceType == Device_Sensor) AddSensorCount++; else if (deviceType == Device_LatencyTester) { AddLatencyTestCount++; AddLatencyTestDisplayCount++; } } bool checkCreateSensor(); void applyProfileToSensorFusion(); // INlines so that they can be easily compiled out. // Does debug ASSERT checks for functions that require BeginFrame. // Also verifies that we are on the right thread. void checkBeginFrameScope(const char* functionName) { OVR_UNUSED1(functionName); // for Release build. OVR_ASSERT_LOG(BeginFrameCalled == true, ("%s called outside ovrHmd_BeginFrame.", functionName)); OVR_ASSERT_LOG(BeginFrameThreadId == OVR::GetCurrentThreadId(), ("%s called on a different thread then ovrHmd_BeginFrame.", functionName)); } void checkRenderingConfigured(const char* functionName) { OVR_UNUSED1(functionName); // for Release build. OVR_ASSERT_LOG(RenderingConfigured == true, ("%s called without ovrHmd_ConfigureRendering.", functionName)); } void checkBeginFrameTimingScope(const char* functionName) { OVR_UNUSED1(functionName); // for Release build. OVR_ASSERT_LOG(BeginFrameTimingCalled == true, ("%s called outside ovrHmd_BeginFrameTiming.", functionName)); } HMDState* getThis() { return this; } void updateLowPersistenceMode(bool lowPersistence) const; void updateLatencyTestForHmd(bool latencyTesting); void updateDK2FeaturesTiedToSensor(bool sensorCreatedJustNow); // Get properties by name. float getFloatValue(const char* propertyName, float defaultVal); bool setFloatValue(const char* propertyName, float value); unsigned getFloatArray(const char* propertyName, float values[], unsigned arraySize); bool setFloatArray(const char* propertyName, float values[], unsigned arraySize); const char* getString(const char* propertyName, const char* defaultVal); public: // Wrapper to support 'const' struct HMDInfoWrapper { HMDInfoWrapper(ovrHmdType hmdType) { HmdTypeEnum t = HmdType_None; if (hmdType == ovrHmd_DK1) t = HmdType_DK1; else if (hmdType == ovrHmd_CrystalCoveProto) t = HmdType_CrystalCoveProto; else if (hmdType == ovrHmd_DK2) t = HmdType_DK2; h = CreateDebugHMDInfo(t); } HMDInfoWrapper(HMDDevice* device) { if (device) device->GetDeviceInfo(&h); } OVR::HMDInfo h; }; // Note: pHMD can be null if we are representing a virtualized debug HMD. Ptr pHMD; // HMDInfo shouldn't change, as its string pointers are passed out. const HMDInfoWrapper HMDInfoW; const OVR::HMDInfo& HMDInfo; const char* pLastError; // Caps enabled for the HMD. unsigned EnabledHmdCaps; // These are the flags actually applied to the Sensor device, // used to track whether SetDisplayReport calls are necessary. unsigned HmdCapsAppliedToSensor; // *** Sensor // Lock used to support thread-safe lifetime access to sensor. Lock DevicesLock; // Atomic integer used as a flag that we should check the sensor device. AtomicInt AddSensorCount; // All of Sensor variables may be modified/used with DevicesLock, with exception that // the {SensorStarted, SensorCreated} can be read outside the lock to see // if device creation check is necessary. // Whether we called StartSensor() and requested sensor caps. volatile bool SensorStarted; volatile bool SensorCreated; // pSensor may still be null or non-running after start if it wasn't yet available Ptr pSensor; // Head unsigned SensorCaps; // SensorFusion state may be accessible without a lock. SensorFusion SFusion; // Vision pose tracker is currently new-allocated OVR_CAPI_VISION_CODE( Vision::PoseTracker* pPoseTracker; ) // Latency tester Ptr pLatencyTester; Util::LatencyTest LatencyUtil; AtomicInt AddLatencyTestCount; bool LatencyTestActive; unsigned char LatencyTestDrawColor[3]; // Using latency tester as debug display Ptr pLatencyTesterDisplay; AtomicInt AddLatencyTestDisplayCount; Util::LatencyTest2 LatencyUtil2; bool LatencyTest2Active; unsigned char LatencyTest2DrawColor[3]; //bool ReadbackColor; // Rendering part FrameTimeManager TimeManager; HMDRenderState RenderState; Ptr pRenderer; // Last timing value reported by BeginFrame. double LastFrameTimeSeconds; // Last timing value reported by GetFrameTime. These are separate since the intended // use is from different threads. TBD: Move to FrameTimeManager? Make atomic? double LastGetFrameTimeSeconds; // Last cached value returned by ovrHmd_GetString/ovrHmd_GetStringArray. char LastGetStringValue[256]; // Debug flag set after ovrHmd_ConfigureRendering succeeds. bool RenderingConfigured; // Set after BeginFrame succeeds, and its corresponding thread id for debug checks. bool BeginFrameCalled; ThreadId BeginFrameThreadId; // Graphics functions are not re-entrant from other threads. ThreadChecker RenderAPIThreadChecker; // bool BeginFrameTimingCalled; // Flags set when we've called BeginEyeRender on a given eye. bool EyeRenderActive[2]; }; }} // namespace OVR::CAPI #endif // OVR_CAPI_HMDState_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp new file mode 100644 index 0000000..d40904c --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_GL_DistortionRenderer.h Content : Distortion renderer header for GL Created : November 11, 2013 Authors : David Borel, Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #include "CAPI_GL_DistortionRenderer.h" #include "CAPI_GL_DistortionShaders.h" #include "../../OVR_CAPI_GL.h" namespace OVR { namespace CAPI { namespace GL { // Distortion pixel shader lookup. // Bit 0: Chroma Correction // Bit 1: Timewarp enum { DistortionVertexShaderBitMask = 3, DistortionVertexShaderCount = DistortionVertexShaderBitMask + 1, DistortionPixelShaderBitMask = 1, DistortionPixelShaderCount = DistortionPixelShaderBitMask + 1 }; struct ShaderInfo { const char* ShaderData; size_t ShaderSize; const ShaderBase::Uniform* ReflectionData; size_t ReflectionSize; }; // Do add a new distortion shader use these macros (with or w/o reflection) #define SI_NOREFL(shader) { shader, sizeof(shader), NULL, 0 } #define SI_REFL__(shader) { shader, sizeof(shader), shader ## _refl, sizeof( shader ## _refl )/sizeof(*(shader ## _refl)) } static ShaderInfo DistortionVertexShaderLookup[DistortionVertexShaderCount] = { SI_REFL__(Distortion_vs), SI_REFL__(DistortionChroma_vs), SI_REFL__(DistortionTimewarp_vs), SI_REFL__(DistortionTimewarpChroma_vs) }; static ShaderInfo DistortionPixelShaderLookup[DistortionPixelShaderCount] = { SI_NOREFL(Distortion_fs), SI_NOREFL(DistortionChroma_fs) }; void DistortionShaderBitIndexCheck() { OVR_COMPILER_ASSERT(ovrDistortionCap_Chromatic == 1); OVR_COMPILER_ASSERT(ovrDistortionCap_TimeWarp == 2); } struct DistortionVertex { Vector2f Pos; Vector2f TexR; Vector2f TexG; Vector2f TexB; Color Col; }; // Vertex type; same format is used for all shapes for simplicity. // Shapes are built by adding vertices to Model. struct LatencyVertex { Vector3f Pos; LatencyVertex (const Vector3f& p) : Pos(p) {} }; //---------------------------------------------------------------------------- // ***** GL::DistortionRenderer DistortionRenderer::DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState) : CAPI::DistortionRenderer(ovrRenderAPI_OpenGL, hmd, timeManager, renderState) , LatencyVAO(0) { DistortionMeshVAOs[0] = 0; DistortionMeshVAOs[1] = 0; } DistortionRenderer::~DistortionRenderer() { destroy(); } // static CAPI::DistortionRenderer* DistortionRenderer::Create(ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState) { #if !defined(OVR_OS_MAC) InitGLExtensions(); #endif return new DistortionRenderer(hmd, timeManager, renderState); } bool DistortionRenderer::Initialize(const ovrRenderAPIConfig* apiConfig, unsigned distortionCaps) { GfxState = *new GraphicsState(); const ovrGLConfig* config = (const ovrGLConfig*)apiConfig; if (!config) { // Cleanup pEyeTextures[0].Clear(); pEyeTextures[1].Clear(); memset(&RParams, 0, sizeof(RParams)); return true; } RParams.Multisample = config->OGL.Header.Multisample; RParams.RTSize = config->OGL.Header.RTSize; #if defined(OVR_OS_WIN32) RParams.Window = (config->OGL.Window) ? config->OGL.Window : GetActiveWindow(); #elif defined(OVR_OS_LINUX) RParams.Disp = (config->OGL.Disp) ? config->OGL.Disp : XOpenDisplay(NULL); RParams.Win = config->OGL.Win; if (!RParams.Win) { int unused; XGetInputFocus(RParams.Disp, &RParams.Win, &unused); } #endif DistortionCaps = distortionCaps; //DistortionWarper.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true); pEyeTextures[0] = *new Texture(&RParams, 0, 0); pEyeTextures[1] = *new Texture(&RParams, 0, 0); initBuffersAndShaders(); return true; } void DistortionRenderer::SubmitEye(int eyeId, ovrTexture* eyeTexture) { // Doesn't do a lot in here?? const ovrGLTexture* tex = (const ovrGLTexture*)eyeTexture; // Write in values eachEye[eyeId].texture = tex->OGL.TexId; if (tex) { // Its only at this point we discover what the viewport of the texture is. // because presumably we allow users to realtime adjust the resolution. eachEye[eyeId].TextureSize = tex->OGL.Header.TextureSize; eachEye[eyeId].RenderViewport = tex->OGL.Header.RenderViewport; const ovrEyeRenderDesc& erd = RState.EyeRenderDesc[eyeId]; ovrHmd_GetRenderScaleAndOffset( erd.Fov, eachEye[eyeId].TextureSize, eachEye[eyeId].RenderViewport, eachEye[eyeId].UVScaleOffset ); pEyeTextures[eyeId]->UpdatePlaceholderTexture(tex->OGL.TexId, tex->OGL.Header.TextureSize); } } void DistortionRenderer::EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor) { if (!TimeManager.NeedDistortionTimeMeasurement()) { if (RState.DistortionCaps & ovrDistortionCap_TimeWarp) { // Wait for timewarp distortion if it is time and Gpu idle FlushGpuAndWaitTillTime(TimeManager.GetFrameTiming().TimewarpPointTime); } renderDistortion(pEyeTextures[0], pEyeTextures[1]); } else { // If needed, measure distortion time so that TimeManager can better estimate // latency-reducing time-warp wait timing. WaitUntilGpuIdle(); double distortionStartTime = ovr_GetTimeInSeconds(); renderDistortion(pEyeTextures[0], pEyeTextures[1]); WaitUntilGpuIdle(); TimeManager.AddDistortionTimeMeasurement(ovr_GetTimeInSeconds() - distortionStartTime); } if(latencyTesterDrawColor) { renderLatencyQuad(latencyTesterDrawColor); } else if(latencyTester2DrawColor) { renderLatencyPixel(latencyTester2DrawColor); } if (swapBuffers) { bool useVsync = ((RState.EnabledHmdCaps & ovrHmdCap_NoVSync) == 0); int swapInterval = (useVsync) ? 1 : 0; #if defined(OVR_OS_WIN32) if (wglGetSwapIntervalEXT() != swapInterval) wglSwapIntervalEXT(swapInterval); HDC dc = GetDC(RParams.Window); BOOL success = SwapBuffers(dc); ReleaseDC(RParams.Window, dc); OVR_ASSERT(success); OVR_UNUSED(success); #elif defined(OVR_OS_MAC) CGLContextObj context = CGLGetCurrentContext(); GLint currentSwapInterval = 0; CGLGetParameter(context, kCGLCPSwapInterval, ¤tSwapInterval); if (currentSwapInterval != swapInterval) CGLSetParameter(context, kCGLCPSwapInterval, &swapInterval); CGLFlushDrawable(context); #elif defined(OVR_OS_LINUX) static const char* extensions = glXQueryExtensionsString(RParams.Disp, 0); static bool supportsVSync = (extensions != NULL && strstr(extensions, "GLX_EXT_swap_control")); if (supportsVSync) { GLuint currentSwapInterval = 0; glXQueryDrawable(RParams.Disp, RParams.Win, GLX_SWAP_INTERVAL_EXT, ¤tSwapInterval); if (currentSwapInterval != swapInterval) glXSwapIntervalEXT(RParams.Disp, RParams.Win, swapInterval); } glXSwapBuffers(RParams.Disp, RParams.Win); #endif } } void DistortionRenderer::WaitUntilGpuIdle() { glFlush(); glFinish(); } double DistortionRenderer::FlushGpuAndWaitTillTime(double absTime) { double initialTime = ovr_GetTimeInSeconds(); if (initialTime >= absTime) return 0.0; glFlush(); glFinish(); double newTime = initialTime; volatile int i; while (newTime < absTime) { for (int j = 0; j < 50; j++) i = 0; newTime = ovr_GetTimeInSeconds(); } // How long we waited return newTime - initialTime; } DistortionRenderer::GraphicsState::GraphicsState() { const char* glVersionString = (const char*)glGetString(GL_VERSION); OVR_DEBUG_LOG(("GL_VERSION STRING: %s", (const char*)glVersionString)); char prefix[64]; bool foundVersion = false; for (int i = 10; i < 30; ++i) { int major = i / 10; int minor = i % 10; OVR_sprintf(prefix, 64, "%d.%d", major, minor); if (strstr(glVersionString, prefix) == glVersionString) { GlMajorVersion = major; GlMinorVersion = minor; foundVersion = true; break; } } if (!foundVersion) { glGetIntegerv(GL_MAJOR_VERSION, &GlMajorVersion); glGetIntegerv(GL_MAJOR_VERSION, &GlMinorVersion); } OVR_ASSERT(GlMajorVersion >= 2); if (GlMajorVersion >= 3) { SupportsVao = true; } else { const char* extensions = (const char*)glGetString(GL_EXTENSIONS); SupportsVao = (strstr("GL_ARB_vertex_array_object", extensions) != NULL); } } void DistortionRenderer::GraphicsState::ApplyBool(GLenum Name, GLint Value) { if (Value != 0) glEnable(Name); else glDisable(Name); } void DistortionRenderer::GraphicsState::Save() { glGetIntegerv(GL_VIEWPORT, Viewport); glGetFloatv(GL_COLOR_CLEAR_VALUE, ClearColor); glGetIntegerv(GL_DEPTH_TEST, &DepthTest); glGetIntegerv(GL_CULL_FACE, &CullFace); glGetIntegerv(GL_CURRENT_PROGRAM, &Program); glGetIntegerv(GL_ACTIVE_TEXTURE, &ActiveTexture); glGetIntegerv(GL_TEXTURE_BINDING_2D, &TextureBinding); glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &VertexArray); glGetIntegerv(GL_FRAMEBUFFER_BINDING, &FrameBufferBinding); glGetIntegerv(GL_BLEND, &Blend); glGetIntegerv(GL_COLOR_WRITEMASK, ColorWritemask); glGetIntegerv(GL_DITHER, &Dither); glGetIntegerv(GL_RASTERIZER_DISCARD, &RasterizerDiscard); if (GlMajorVersion >= 3 && GlMajorVersion >= 2) glGetIntegerv(GL_SAMPLE_MASK, &SampleMask); glGetIntegerv(GL_SCISSOR_TEST, &ScissorTest); IsValid = true; } void DistortionRenderer::GraphicsState::Restore() { // Don't allow restore-before-save. if (!IsValid) return; glViewport(Viewport[0], Viewport[1], Viewport[2], Viewport[3]); glClearColor(ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]); ApplyBool(GL_DEPTH_TEST, DepthTest); ApplyBool(GL_CULL_FACE, CullFace); glUseProgram(Program); glActiveTexture(ActiveTexture); glBindTexture(GL_TEXTURE_2D, TextureBinding); if (SupportsVao) glBindVertexArray(VertexArray); glBindFramebuffer(GL_FRAMEBUFFER, FrameBufferBinding); ApplyBool(GL_BLEND, Blend); glColorMask((GLboolean)ColorWritemask[0], (GLboolean)ColorWritemask[1], (GLboolean)ColorWritemask[2], (GLboolean)ColorWritemask[3]); ApplyBool(GL_DITHER, Dither); ApplyBool(GL_RASTERIZER_DISCARD, RasterizerDiscard); if (GlMajorVersion >= 3 && GlMajorVersion >= 2) ApplyBool(GL_SAMPLE_MASK, SampleMask); ApplyBool(GL_SCISSOR_TEST, ScissorTest); } void DistortionRenderer::initBuffersAndShaders() { for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { // Allocate & generate distortion mesh vertices. ovrDistortionMesh meshData; // double startT = ovr_GetTimeInSeconds(); if (!ovrHmd_CreateDistortionMesh( HMD, RState.EyeRenderDesc[eyeNum].Eye, RState.EyeRenderDesc[eyeNum].Fov, RState.DistortionCaps, &meshData) ) { OVR_ASSERT(false); continue; } // Now parse the vertex data and create a render ready vertex buffer from it DistortionVertex * pVBVerts = (DistortionVertex*)OVR_ALLOC ( sizeof(DistortionVertex) * meshData.VertexCount ); DistortionVertex * pCurVBVert = pVBVerts; ovrDistortionVertex* pCurOvrVert = meshData.pVertexData; for ( unsigned vertNum = 0; vertNum < meshData.VertexCount; vertNum++ ) { pCurVBVert->Pos.x = pCurOvrVert->Pos.x; pCurVBVert->Pos.y = pCurOvrVert->Pos.y; pCurVBVert->TexR = (*(Vector2f*)&pCurOvrVert->TexR); pCurVBVert->TexG = (*(Vector2f*)&pCurOvrVert->TexG); pCurVBVert->TexB = (*(Vector2f*)&pCurOvrVert->TexB); // Convert [0.0f,1.0f] to [0,255] pCurVBVert->Col.R = (OVR::UByte)( pCurOvrVert->VignetteFactor * 255.99f ); pCurVBVert->Col.G = pCurVBVert->Col.R; pCurVBVert->Col.B = pCurVBVert->Col.R; pCurVBVert->Col.A = (OVR::UByte)( pCurOvrVert->TimeWarpFactor * 255.99f );; pCurOvrVert++; pCurVBVert++; } DistortionMeshVBs[eyeNum] = *new Buffer(&RParams); DistortionMeshVBs[eyeNum]->Data ( Buffer_Vertex | Buffer_ReadOnly, pVBVerts, sizeof(DistortionVertex) * meshData.VertexCount ); DistortionMeshIBs[eyeNum] = *new Buffer(&RParams); DistortionMeshIBs[eyeNum]->Data ( Buffer_Index | Buffer_ReadOnly, meshData.pIndexData, ( sizeof(SInt16) * meshData.IndexCount ) ); OVR_FREE ( pVBVerts ); ovrHmd_DestroyDistortionMesh( &meshData ); } initShaders(); } void DistortionRenderer::renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture) { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); glBindFramebuffer(GL_FRAMEBUFFER, 0); setViewport( Recti(0,0, RParams.RTSize.w, RParams.RTSize.h) ); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); glDisable(GL_DITHER); glDisable(GL_RASTERIZER_DISCARD); if (glState->GlMajorVersion >= 3 && glState->GlMajorVersion >= 2) glDisable(GL_SAMPLE_MASK); glDisable(GL_SCISSOR_TEST); glClearColor( RState.ClearColor[0], RState.ClearColor[1], RState.ClearColor[2], RState.ClearColor[3] ); glClear(GL_COLOR_BUFFER_BIT); for (int eyeNum = 0; eyeNum < 2; eyeNum++) { ShaderFill distortionShaderFill(DistortionShader); distortionShaderFill.SetTexture(0, eyeNum == 0 ? leftEyeTexture : rightEyeTexture); DistortionShader->SetUniform2f("EyeToSourceUVScale", eachEye[eyeNum].UVScaleOffset[0].x, eachEye[eyeNum].UVScaleOffset[0].y); DistortionShader->SetUniform2f("EyeToSourceUVOffset", eachEye[eyeNum].UVScaleOffset[1].x, eachEye[eyeNum].UVScaleOffset[1].y); if (DistortionCaps & ovrDistortionCap_TimeWarp) { ovrMatrix4f timeWarpMatrices[2]; ovrHmd_GetEyeTimewarpMatrices(HMD, (ovrEyeType)eyeNum, RState.EyeRenderPoses[eyeNum], timeWarpMatrices); // Feed identity like matrices in until we get proper timewarp calculation going on DistortionShader->SetUniform4x4f("EyeRotationStart", Matrix4f(timeWarpMatrices[0]).Transposed()); DistortionShader->SetUniform4x4f("EyeRotationEnd", Matrix4f(timeWarpMatrices[1]).Transposed()); renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum], 0, (int)DistortionMeshIBs[eyeNum]->GetSize()/2, Prim_Triangles, &DistortionMeshVAOs[eyeNum], true); } else { renderPrimitives(&distortionShaderFill, DistortionMeshVBs[eyeNum], DistortionMeshIBs[eyeNum], 0, (int)DistortionMeshIBs[eyeNum]->GetSize()/2, Prim_Triangles, &DistortionMeshVAOs[eyeNum], true); } } } void DistortionRenderer::createDrawQuad() { const int numQuadVerts = 4; LatencyTesterQuadVB = *new Buffer(&RParams); if(!LatencyTesterQuadVB) { return; } LatencyTesterQuadVB->Data(Buffer_Vertex, NULL, numQuadVerts * sizeof(LatencyVertex)); LatencyVertex* vertices = (LatencyVertex*)LatencyTesterQuadVB->Map(0, numQuadVerts * sizeof(LatencyVertex), Map_Discard); if(!vertices) { OVR_ASSERT(false); // failed to lock vertex buffer return; } const float left = -1.0f; const float top = -1.0f; const float right = 1.0f; const float bottom = 1.0f; vertices[0] = LatencyVertex(Vector3f(left, top, 0.0f)); vertices[1] = LatencyVertex(Vector3f(left, bottom, 0.0f)); vertices[2] = LatencyVertex(Vector3f(right, top, 0.0f)); vertices[3] = LatencyVertex(Vector3f(right, bottom, 0.0f)); LatencyTesterQuadVB->Unmap(vertices); } void DistortionRenderer::renderLatencyQuad(unsigned char* latencyTesterDrawColor) { const int numQuadVerts = 4; if(!LatencyTesterQuadVB) { createDrawQuad(); } ShaderFill quadFill(SimpleQuadShader); //quadFill.SetInputLayout(SimpleQuadVertexIL); setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h)); SimpleQuadShader->SetUniform2f("Scale", 0.2f, 0.2f); SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterDrawColor[0] / 255.99f, (float)latencyTesterDrawColor[0] / 255.99f, (float)latencyTesterDrawColor[0] / 255.99f, 1.0f); for(int eyeNum = 0; eyeNum < 2; eyeNum++) { SimpleQuadShader->SetUniform2f("PositionOffset", eyeNum == 0 ? -0.4f : 0.4f, 0.0f); renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false); } } void DistortionRenderer::renderLatencyPixel(unsigned char* latencyTesterPixelColor) { const int numQuadVerts = 4; if(!LatencyTesterQuadVB) { createDrawQuad(); } ShaderFill quadFill(SimpleQuadShader); setViewport(Recti(0,0, RParams.RTSize.w, RParams.RTSize.h)); SimpleQuadShader->SetUniform4f("Color", (float)latencyTesterPixelColor[0] / 255.99f, (float)latencyTesterPixelColor[0] / 255.99f, (float)latencyTesterPixelColor[0] / 255.99f, 1.0f); Vector2f scale(2.0f / RParams.RTSize.w, 2.0f / RParams.RTSize.h); SimpleQuadShader->SetUniform2f("Scale", scale.x, scale.y); SimpleQuadShader->SetUniform2f("PositionOffset", 1.0f, 1.0f); renderPrimitives(&quadFill, LatencyTesterQuadVB, NULL, 0, numQuadVerts, Prim_TriangleStrip, &LatencyVAO, false); } void DistortionRenderer::renderPrimitives( const ShaderFill* fill, Buffer* vertices, Buffer* indices, int offset, int count, PrimitiveType rprim, GLuint* vao, bool isDistortionMesh) { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); GLenum prim; switch (rprim) { case Prim_Triangles: prim = GL_TRIANGLES; break; case Prim_Lines: prim = GL_LINES; break; case Prim_TriangleStrip: prim = GL_TRIANGLE_STRIP; break; default: OVR_ASSERT(false); return; } fill->Set(); GLuint prog = fill->GetShaders()->Prog; if (vao != NULL) { if (*vao != 0) { glBindVertexArray(*vao); if (isDistortionMesh) glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); else glDrawArrays(prim, 0, count); } else { if (glState->SupportsVao) { glGenVertexArrays(1, vao); glBindVertexArray(*vao); } int attributeCount = (isDistortionMesh) ? 5 : 1; int* locs = new int[attributeCount]; glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer); if (isDistortionMesh) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer); locs[0] = glGetAttribLocation(prog, "Position"); locs[1] = glGetAttribLocation(prog, "Color"); locs[2] = glGetAttribLocation(prog, "TexCoord0"); locs[3] = glGetAttribLocation(prog, "TexCoord1"); locs[4] = glGetAttribLocation(prog, "TexCoord2"); glVertexAttribPointer(locs[0], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, Pos)); glVertexAttribPointer(locs[1], 4, GL_UNSIGNED_BYTE, true, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, Col)); glVertexAttribPointer(locs[2], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, TexR)); glVertexAttribPointer(locs[3], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, TexG)); glVertexAttribPointer(locs[4], 2, GL_FLOAT, false, sizeof(DistortionVertex), reinterpret_cast(offset)+offsetof(DistortionVertex, TexB)); } else { locs[0] = glGetAttribLocation(prog, "Position"); glVertexAttribPointer(locs[0], 3, GL_FLOAT, false, sizeof(LatencyVertex), reinterpret_cast(offset)+offsetof(LatencyVertex, Pos)); } for (int i = 0; i < attributeCount; ++i) glEnableVertexAttribArray(locs[i]); if (isDistortionMesh) glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL); else glDrawArrays(prim, 0, count); if (!glState->SupportsVao) { for (int i = 0; i < attributeCount; ++i) glDisableVertexAttribArray(locs[i]); } delete[] locs; } } } void DistortionRenderer::setViewport(const Recti& vp) { glViewport(vp.x, vp.y, vp.w, vp.h); } void DistortionRenderer::initShaders() { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); const char* shaderPrefix = (glState->GlMajorVersion < 3 || (glState->GlMajorVersion == 3 && glState->GlMinorVersion < 2)) ? glsl2Prefix : glsl3Prefix; { ShaderInfo vsInfo = DistortionVertexShaderLookup[DistortionVertexShaderBitMask & DistortionCaps]; size_t vsSize = strlen(shaderPrefix)+vsInfo.ShaderSize; char* vsSource = new char[vsSize]; OVR_strcpy(vsSource, vsSize, shaderPrefix); OVR_strcat(vsSource, vsSize, vsInfo.ShaderData); Ptr vs = *new GL::VertexShader( &RParams, (void*)vsSource, vsSize, vsInfo.ReflectionData, vsInfo.ReflectionSize); DistortionShader = *new ShaderSet; DistortionShader->SetShader(vs); delete[](vsSource); ShaderInfo psInfo = DistortionPixelShaderLookup[DistortionPixelShaderBitMask & DistortionCaps]; size_t psSize = strlen(shaderPrefix)+psInfo.ShaderSize; char* psSource = new char[psSize]; OVR_strcpy(psSource, psSize, shaderPrefix); OVR_strcat(psSource, psSize, psInfo.ShaderData); Ptr ps = *new GL::FragmentShader( &RParams, (void*)psSource, psSize, psInfo.ReflectionData, psInfo.ReflectionSize); DistortionShader->SetShader(ps); delete[](psSource); } { size_t vsSize = strlen(shaderPrefix)+sizeof(SimpleQuad_vs); char* vsSource = new char[vsSize]; OVR_strcpy(vsSource, vsSize, shaderPrefix); OVR_strcat(vsSource, vsSize, SimpleQuad_vs); Ptr vs = *new GL::VertexShader( &RParams, (void*)vsSource, vsSize, SimpleQuad_vs_refl, sizeof(SimpleQuad_vs_refl) / sizeof(SimpleQuad_vs_refl[0])); SimpleQuadShader = *new ShaderSet; SimpleQuadShader->SetShader(vs); delete[](vsSource); size_t psSize = strlen(shaderPrefix)+sizeof(SimpleQuad_fs); char* psSource = new char[psSize]; OVR_strcpy(psSource, psSize, shaderPrefix); OVR_strcat(psSource, psSize, SimpleQuad_fs); Ptr ps = *new GL::FragmentShader( &RParams, (void*)psSource, psSize, SimpleQuad_fs_refl, sizeof(SimpleQuad_fs_refl) / sizeof(SimpleQuad_fs_refl[0])); SimpleQuadShader->SetShader(ps); delete[](psSource); } } void DistortionRenderer::destroy() { GraphicsState* glState = (GraphicsState*)GfxState.GetPtr(); for(int eyeNum = 0; eyeNum < 2; eyeNum++) { if (glState->SupportsVao) glDeleteVertexArrays(1, &DistortionMeshVAOs[eyeNum]); DistortionMeshVAOs[eyeNum] = 0; DistortionMeshVBs[eyeNum].Clear(); DistortionMeshIBs[eyeNum].Clear(); } if (DistortionShader) { DistortionShader->UnsetShader(Shader_Vertex); DistortionShader->UnsetShader(Shader_Pixel); DistortionShader.Clear(); } LatencyTesterQuadVB.Clear(); LatencyVAO = 0; } }}} // OVR::CAPI::GL \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h new file mode 100644 index 0000000..31c320c --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionRenderer.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_GL_DistortionRenderer.h Content : Distortion renderer header for GL Created : November 11, 2013 Authors : David Borel, Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_CAPI_GL_DistortionRenderer_h #define OVR_CAPI_GL_DistortionRenderer_h #include "../CAPI_DistortionRenderer.h" #include "../../Kernel/OVR_Log.h" #include "CAPI_GL_Util.h" namespace OVR { namespace CAPI { namespace GL { // ***** GL::DistortionRenderer // Implementation of DistortionRenderer for GL. class DistortionRenderer : public CAPI::DistortionRenderer { public: DistortionRenderer(ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState); ~DistortionRenderer(); // Creation function for the device. static CAPI::DistortionRenderer* Create(ovrHmd hmd, FrameTimeManager& timeManager, const HMDRenderState& renderState); // ***** Public DistortionRenderer interface virtual bool Initialize(const ovrRenderAPIConfig* apiConfig, unsigned distortionCaps); virtual void SubmitEye(int eyeId, ovrTexture* eyeTexture); virtual void EndFrame(bool swapBuffers, unsigned char* latencyTesterDrawColor, unsigned char* latencyTester2DrawColor); void WaitUntilGpuIdle(); // Similar to ovr_WaitTillTime but it also flushes GPU. // Note, it exits when time expires, even if GPU is not in idle state yet. double FlushGpuAndWaitTillTime(double absTime); protected: class GraphicsState : public CAPI::DistortionRenderer::GraphicsState { public: GraphicsState(); virtual void Save(); virtual void Restore(); protected: void ApplyBool(GLenum Name, GLint Value); public: GLint GlMajorVersion; GLint GlMinorVersion; bool SupportsVao; GLint Viewport[4]; GLfloat ClearColor[4]; GLint DepthTest; GLint CullFace; GLint Program; GLint ActiveTexture; GLint TextureBinding; GLint VertexArray; GLint FrameBufferBinding; GLint Blend; GLint ColorWritemask[4]; GLint Dither; GLint Fog; GLint Lighting; GLint RasterizerDiscard; GLint RenderMode; GLint SampleMask; GLint ScissorTest; GLfloat ZoomX; GLfloat ZoomY; }; // TBD: Should we be using oe from RState instead? unsigned DistortionCaps; struct FOR_EACH_EYE { FOR_EACH_EYE() : TextureSize(0), RenderViewport(Sizei(0)) { } #if 0 IDirect3DVertexBuffer9 * dxVerts; IDirect3DIndexBuffer9 * dxIndices; #endif int numVerts; int numIndices; GLuint texture; ovrVector2f UVScaleOffset[2]; Sizei TextureSize; Recti RenderViewport; } eachEye[2]; // GL context and utility variables. RenderParams RParams; // Helpers void initBuffersAndShaders(); void initShaders(); void initFullscreenQuad(); void destroy(); void setViewport(const Recti& vp); void renderDistortion(Texture* leftEyeTexture, Texture* rightEyeTexture); void renderPrimitives(const ShaderFill* fill, Buffer* vertices, Buffer* indices, int offset, int count, PrimitiveType rprim, GLuint* vao, bool isDistortionMesh); void createDrawQuad(); void renderLatencyQuad(unsigned char* latencyTesterDrawColor); void renderLatencyPixel(unsigned char* latencyTesterPixelColor); Ptr pEyeTextures[2]; Ptr DistortionMeshVBs[2]; // one per-eye Ptr DistortionMeshIBs[2]; // one per-eye GLuint DistortionMeshVAOs[2]; // one per-eye Ptr DistortionShader; struct StandardUniformData { Matrix4f Proj; Matrix4f View; } StdUniforms; GLuint LatencyVAO; Ptr LatencyTesterQuadVB; Ptr SimpleQuadShader; Ptr CurRenderTarget; Array > DepthBuffers; GLuint CurrentFbo; GLint SavedViewport[4]; GLfloat SavedClearColor[4]; GLint SavedDepthTest; GLint SavedCullFace; GLint SavedProgram; GLint SavedActiveTexture; GLint SavedBoundTexture; GLint SavedVertexArray; GLint SavedBoundFrameBuffer; }; }}} // OVR::CAPI::GL #endif // OVR_CAPI_GL_DistortionRenderer_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h new file mode 100644 index 0000000..aa60554 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_DistortionShaders.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_GL_Shaders.h Content : Distortion shader header for GL Created : November 11, 2013 Authors : David Borel, Volga Aksoy Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_CAPI_GL_Shaders_h #define OVR_CAPI_GL_Shaders_h #include "CAPI_GL_Util.h" namespace OVR { namespace CAPI { namespace GL { static const char glsl2Prefix[] = "#version 110\n" "#extension GL_ARB_shader_texture_lod : enable\n" "#define _FRAGCOLOR_DECLARATION\n" "#define _VS_IN attribute\n" "#define _VS_OUT varying\n" "#define _FS_IN varying\n" "#define _TEXTURELOD texture2DLod\n" "#define _FRAGCOLOR gl_FragColor\n"; static const char glsl3Prefix[] = "#version 150\n" "#define _FRAGCOLOR_DECLARATION out vec4 FragColor;\n" "#define _VS_IN in\n" "#define _VS_OUT out\n" "#define _FS_IN in\n" "#define _TEXTURELOD textureLod\n" "#define _FRAGCOLOR FragColor\n"; static const char SimpleQuad_vs[] = "uniform vec2 PositionOffset;\n" "uniform vec2 Scale;\n" "_VS_IN vec3 Position;\n" "void main()\n" "{\n" " gl_Position = vec4(Position.xy * Scale + PositionOffset, 0.5, 1.0);\n" "}\n"; const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuad_vs_refl[] = { { "PositionOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 }, { "Scale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 }, }; static const char SimpleQuad_fs[] = "uniform vec4 Color;\n" "_FRAGCOLOR_DECLARATION\n" "void main()\n" "{\n" " _FRAGCOLOR = Color;\n" "}\n"; const OVR::CAPI::GL::ShaderBase::Uniform SimpleQuad_fs_refl[] = { { "Color", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 16 }, }; static const char Distortion_vs[] = "uniform vec2 EyeToSourceUVScale;\n" "uniform vec2 EyeToSourceUVOffset;\n" "_VS_IN vec2 Position;\n" "_VS_IN vec4 Color;\n" "_VS_IN vec2 TexCoord0;\n" "_VS_OUT vec4 oColor;\n" "_VS_OUT vec2 oTexCoord0;\n" "void main()\n" "{\n" " gl_Position.x = Position.x;\n" " gl_Position.y = Position.y;\n" " gl_Position.z = 0.5;\n" " gl_Position.w = 1.0;\n" // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion). // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye) " oTexCoord0 = TexCoord0 * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord0.y = 1.0 - oTexCoord0.y;\n" " oColor = Color;\n" // Used for vignette fade. "}\n"; const OVR::CAPI::GL::ShaderBase::Uniform Distortion_vs_refl[] = { { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 }, { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 }, }; static const char Distortion_fs[] = "uniform sampler2D Texture0;\n" "_FS_IN vec4 oColor;\n" "_FS_IN vec2 oTexCoord0;\n" "_FRAGCOLOR_DECLARATION\n" "void main()\n" "{\n" " _FRAGCOLOR = _TEXTURELOD(Texture0, oTexCoord0, 0.0);\n" " _FRAGCOLOR.a = 1.0;\n" "}\n"; static const char DistortionTimewarp_vs[] = "uniform vec2 EyeToSourceUVScale;\n" "uniform vec2 EyeToSourceUVOffset;\n" "uniform mat4 EyeRotationStart;\n" "uniform mat4 EyeRotationEnd;\n" "_VS_IN vec2 Position;\n" "_VS_IN vec4 Color;\n" "_VS_IN vec2 TexCoord0;\n" "_FS_IN vec4 oColor;\n" "_FS_IN vec2 oTexCoord0;\n" "void main()\n" "{\n" " gl_Position.x = Position.x;\n" " gl_Position.y = Position.y;\n" " gl_Position.z = 0.0;\n" " gl_Position.w = 1.0;\n" // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion). // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD. " vec3 TanEyeAngle = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );\n" // Accurate time warp lerp vs. faster #if 1 // Apply the two 3x3 timewarp rotations to these vectors. " vec3 TransformedStart = (EyeRotationStart * vec4(TanEyeAngle, 0)).xyz;\n" " vec3 TransformedEnd = (EyeRotationEnd * vec4(TanEyeAngle, 0)).xyz;\n" // And blend between them. " vec3 Transformed = mix ( TransformedStart, TransformedEnd, Color.a );\n" #else " mat4 EyeRotation = mix ( EyeRotationStart, EyeRotationEnd, Color.a );\n" " vec3 Transformed = EyeRotation * TanEyeAngle;\n" #endif // Project them back onto the Z=1 plane of the rendered images. " float RecipZ = 1.0 / Transformed.z;\n" " vec2 Flattened = vec2 ( Transformed.x * RecipZ, Transformed.y * RecipZ );\n" // These are now still in TanEyeAngle space. // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye) " vec2 SrcCoord = Flattened * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord0 = SrcCoord;\n" " oTexCoord0.y = 1.0-oTexCoord0.y;\n" " oColor = vec4(Color.r, Color.r, Color.r, Color.r);\n" // Used for vignette fade. "}\n"; const OVR::CAPI::GL::ShaderBase::Uniform DistortionTimewarp_vs_refl[] = { { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 }, { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 }, }; static const char DistortionChroma_vs[] = "uniform vec2 EyeToSourceUVScale;\n" "uniform vec2 EyeToSourceUVOffset;\n" "_VS_IN vec2 Position;\n" "_VS_IN vec4 Color;\n" "_VS_IN vec2 TexCoord0;\n" "_VS_IN vec2 TexCoord1;\n" "_VS_IN vec2 TexCoord2;\n" "_VS_OUT vec4 oColor;\n" "_VS_OUT vec2 oTexCoord0;\n" "_VS_OUT vec2 oTexCoord1;\n" "_VS_OUT vec2 oTexCoord2;\n" "void main()\n" "{\n" " gl_Position.x = Position.x;\n" " gl_Position.y = Position.y;\n" " gl_Position.z = 0.5;\n" " gl_Position.w = 1.0;\n" // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion). // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye) " oTexCoord0 = TexCoord0 * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord0.y = 1.0-oTexCoord0.y;\n" " oTexCoord1 = TexCoord1 * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord1.y = 1.0-oTexCoord1.y;\n" " oTexCoord2 = TexCoord2 * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord2.y = 1.0-oTexCoord2.y;\n" " oColor = Color;\n" // Used for vignette fade. "}\n"; const OVR::CAPI::GL::ShaderBase::Uniform DistortionChroma_vs_refl[] = { { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 }, { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 }, }; static const char DistortionChroma_fs[] = "uniform sampler2D Texture0;\n" "_FS_IN vec4 oColor;\n" "_FS_IN vec2 oTexCoord0;\n" "_FS_IN vec2 oTexCoord1;\n" "_FS_IN vec2 oTexCoord2;\n" "_FRAGCOLOR_DECLARATION\n" "void main()\n" "{\n" " float ResultR = _TEXTURELOD(Texture0, oTexCoord0, 0.0).r;\n" " float ResultG = _TEXTURELOD(Texture0, oTexCoord1, 0.0).g;\n" " float ResultB = _TEXTURELOD(Texture0, oTexCoord2, 0.0).b;\n" " _FRAGCOLOR = vec4(ResultR * oColor.r, ResultG * oColor.g, ResultB * oColor.b, 1.0);\n" "}\n"; static const char DistortionTimewarpChroma_vs[] = "uniform vec2 EyeToSourceUVScale;\n" "uniform vec2 EyeToSourceUVOffset;\n" "uniform mat4 EyeRotationStart;\n" "uniform mat4 EyeRotationEnd;\n" "_VS_IN vec2 Position;\n" "_VS_IN vec4 Color;\n" "_VS_IN vec2 TexCoord0;\n" "_VS_IN vec2 TexCoord1;\n" "_VS_IN vec2 TexCoord2;\n" "_VS_OUT vec4 oColor;\n" "_VS_OUT vec2 oTexCoord0;\n" "_VS_OUT vec2 oTexCoord1;\n" "_VS_OUT vec2 oTexCoord2;\n" "void main()\n" "{\n" " gl_Position.x = Position.x;\n" " gl_Position.y = Position.y;\n" " gl_Position.z = 0.0;\n" " gl_Position.w = 1.0;\n" // Vertex inputs are in TanEyeAngle space for the R,G,B channels (i.e. after chromatic aberration and distortion). // These are now "real world" vectors in direction (x,y,1) relative to the eye of the HMD. " vec3 TanEyeAngleR = vec3 ( TexCoord0.x, TexCoord0.y, 1.0 );\n" " vec3 TanEyeAngleG = vec3 ( TexCoord1.x, TexCoord1.y, 1.0 );\n" " vec3 TanEyeAngleB = vec3 ( TexCoord2.x, TexCoord2.y, 1.0 );\n" // Accurate time warp lerp vs. faster #if 1 // Apply the two 3x3 timewarp rotations to these vectors. " vec3 TransformedRStart = (EyeRotationStart * vec4(TanEyeAngleR, 0)).xyz;\n" " vec3 TransformedGStart = (EyeRotationStart * vec4(TanEyeAngleG, 0)).xyz;\n" " vec3 TransformedBStart = (EyeRotationStart * vec4(TanEyeAngleB, 0)).xyz;\n" " vec3 TransformedREnd = (EyeRotationEnd * vec4(TanEyeAngleR, 0)).xyz;\n" " vec3 TransformedGEnd = (EyeRotationEnd * vec4(TanEyeAngleG, 0)).xyz;\n" " vec3 TransformedBEnd = (EyeRotationEnd * vec4(TanEyeAngleB, 0)).xyz;\n" // And blend between them. " vec3 TransformedR = mix ( TransformedRStart, TransformedREnd, Color.a );\n" " vec3 TransformedG = mix ( TransformedGStart, TransformedGEnd, Color.a );\n" " vec3 TransformedB = mix ( TransformedBStart, TransformedBEnd, Color.a );\n" #else " mat3 EyeRotation;\n" " EyeRotation[0] = mix ( EyeRotationStart[0], EyeRotationEnd[0], Color.a ).xyz;\n" " EyeRotation[1] = mix ( EyeRotationStart[1], EyeRotationEnd[1], Color.a ).xyz;\n" " EyeRotation[2] = mix ( EyeRotationStart[2], EyeRotationEnd[2], Color.a ).xyz;\n" " vec3 TransformedR = EyeRotation * TanEyeAngleR;\n" " vec3 TransformedG = EyeRotation * TanEyeAngleG;\n" " vec3 TransformedB = EyeRotation * TanEyeAngleB;\n" #endif // Project them back onto the Z=1 plane of the rendered images. " float RecipZR = 1.0 / TransformedR.z;\n" " float RecipZG = 1.0 / TransformedG.z;\n" " float RecipZB = 1.0 / TransformedB.z;\n" " vec2 FlattenedR = vec2 ( TransformedR.x * RecipZR, TransformedR.y * RecipZR );\n" " vec2 FlattenedG = vec2 ( TransformedG.x * RecipZG, TransformedG.y * RecipZG );\n" " vec2 FlattenedB = vec2 ( TransformedB.x * RecipZB, TransformedB.y * RecipZB );\n" // These are now still in TanEyeAngle space. // Scale them into the correct [0-1],[0-1] UV lookup space (depending on eye) " vec2 SrcCoordR = FlattenedR * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " vec2 SrcCoordG = FlattenedG * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " vec2 SrcCoordB = FlattenedB * EyeToSourceUVScale + EyeToSourceUVOffset;\n" " oTexCoord0 = SrcCoordR;\n" " oTexCoord0.y = 1.0-oTexCoord0.y;\n" " oTexCoord1 = SrcCoordG;\n" " oTexCoord1.y = 1.0-oTexCoord1.y;\n" " oTexCoord2 = SrcCoordB;\n" " oTexCoord2.y = 1.0-oTexCoord2.y;\n" " oColor = vec4(Color.r, Color.r, Color.r, Color.r);\n" // Used for vignette fade. "}\n"; const OVR::CAPI::GL::ShaderBase::Uniform DistortionTimewarpChroma_vs_refl[] = { { "EyeToSourceUVScale", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 0, 8 }, { "EyeToSourceUVOffset", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 8, 8 }, { "EyeRotationStart", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 16, 64 }, { "EyeRotationEnd", OVR::CAPI::GL::ShaderBase::VARTYPE_FLOAT, 80, 64 }, }; }}} // OVR::CAPI::GL #endif // OVR_CAPI_GL_Shaders_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp new file mode 100644 index 0000000..d3e4afa --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_Util.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Render_GL_Device.cpp Content : RenderDevice implementation for OpenGL Created : September 10, 2012 Authors : David Borel, Andrew Reisse Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software 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_GL_Util.h" #include "../../Kernel/OVR_Log.h" #include namespace OVR { namespace CAPI { namespace GL { // GL Hooks for non-Mac. #if !defined(OVR_OS_MAC) #if defined(OVR_OS_WIN32) PFNWGLGETPROCADDRESS wglGetProcAddress; PFNGLENABLEPROC glEnable; PFNGLDISABLEPROC glDisable; PFNGLGETFLOATVPROC glGetFloatv; PFNGLGETINTEGERVPROC glGetIntegerv; PFNGLGETSTRINGPROC glGetString; PFNGLCOLORMASKPROC glColorMask; PFNGLCLEARPROC glClear; PFNGLCLEARCOLORPROC glClearColor; PFNGLCLEARDEPTHPROC glClearDepth; PFNGLVIEWPORTPROC glViewport; PFNGLDRAWELEMENTSPROC glDrawElements; PFNGLTEXPARAMETERIPROC glTexParameteri; PFNGLFLUSHPROC glFlush; PFNGLFINISHPROC glFinish; PFNGLDRAWARRAYSPROC glDrawArrays; PFNGLGENTEXTURESPROC glGenTextures; PFNGLDELETETEXTURESPROC glDeleteTextures; PFNGLBINDTEXTUREPROC glBindTexture; PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; #elif defined(OVR_OS_LINUX) PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; #endif PFNGLDELETESHADERPROC glDeleteShader; PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; PFNGLACTIVETEXTUREPROC glActiveTexture; PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; PFNGLBINDBUFFERPROC glBindBuffer; PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; PFNGLDELETEBUFFERSPROC glDeleteBuffers; PFNGLBUFFERDATAPROC glBufferData; PFNGLGENBUFFERSPROC glGenBuffers; PFNGLMAPBUFFERPROC glMapBuffer; PFNGLUNMAPBUFFERPROC glUnmapBuffer; PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; PFNGLGETSHADERIVPROC glGetShaderiv; PFNGLCOMPILESHADERPROC glCompileShader; PFNGLSHADERSOURCEPROC glShaderSource; PFNGLCREATESHADERPROC glCreateShader; PFNGLCREATEPROGRAMPROC glCreateProgram; PFNGLATTACHSHADERPROC glAttachShader; PFNGLDETACHSHADERPROC glDetachShader; PFNGLDELETEPROGRAMPROC glDeleteProgram; PFNGLUNIFORM1IPROC glUniform1i; PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; PFNGLUSEPROGRAMPROC glUseProgram; PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; PFNGLGETPROGRAMIVPROC glGetProgramiv; PFNGLLINKPROGRAMPROC glLinkProgram; PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; PFNGLUNIFORM4FVPROC glUniform4fv; PFNGLUNIFORM3FVPROC glUniform3fv; PFNGLUNIFORM2FVPROC glUniform2fv; PFNGLUNIFORM1FVPROC glUniform1fv; PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; PFNGLBINDVERTEXARRAYPROC glBindVertexArray; #if defined(OVR_OS_WIN32) void* GetFunction(const char* functionName) { return wglGetProcAddress(functionName); } #else void (*GetFunction(const char *functionName))( void ) { return glXGetProcAddress((GLubyte*)functionName); } #endif void InitGLExtensions() { if (glGenVertexArrays) return; #if defined(OVR_OS_WIN32) HINSTANCE hInst = LoadLibrary(L"Opengl32.dll"); if (!hInst) return; glGetFloatv = (PFNGLGETFLOATVPROC) GetProcAddress(hInst, "glGetFloatv"); glGetIntegerv = (PFNGLGETINTEGERVPROC) GetProcAddress(hInst, "glGetIntegerv"); glGetString = (PFNGLGETSTRINGPROC) GetProcAddress(hInst, "glGetString"); glEnable = (PFNGLENABLEPROC) GetProcAddress(hInst, "glEnable"); glDisable = (PFNGLDISABLEPROC) GetProcAddress(hInst, "glDisable"); glColorMask = (PFNGLCOLORMASKPROC) GetProcAddress(hInst, "glColorMask"); glClear = (PFNGLCLEARPROC) GetProcAddress(hInst, "glClear" ); glClearColor = (PFNGLCLEARCOLORPROC) GetProcAddress(hInst, "glClearColor"); glClearDepth = (PFNGLCLEARDEPTHPROC) GetProcAddress(hInst, "glClearDepth"); glViewport = (PFNGLVIEWPORTPROC) GetProcAddress(hInst, "glViewport"); glFlush = (PFNGLFLUSHPROC) GetProcAddress(hInst, "glFlush"); glFinish = (PFNGLFINISHPROC) GetProcAddress(hInst, "glFinish"); glDrawArrays = (PFNGLDRAWARRAYSPROC) GetProcAddress(hInst, "glDrawArrays"); glDrawElements = (PFNGLDRAWELEMENTSPROC) GetProcAddress(hInst, "glDrawElements"); glGenTextures = (PFNGLGENTEXTURESPROC) GetProcAddress(hInst,"glGenTextures"); glDeleteTextures = (PFNGLDELETETEXTURESPROC) GetProcAddress(hInst,"glDeleteTextures"); glBindTexture = (PFNGLBINDTEXTUREPROC) GetProcAddress(hInst,"glBindTexture"); glTexParameteri = (PFNGLTEXPARAMETERIPROC) GetProcAddress(hInst, "glTexParameteri"); wglGetProcAddress = (PFNWGLGETPROCADDRESS) GetProcAddress(hInst, "wglGetProcAddress"); wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) GetFunction("wglGetSwapIntervalEXT"); wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) GetFunction("wglSwapIntervalEXT"); #elif defined(OVR_OS_LINUX) glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) GetFunction("glXSwapIntervalEXT"); #endif glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) GetFunction("glBindFramebufferEXT"); glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) GetFunction("glGenVertexArrays"); glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) GetFunction("glDeleteVertexArrays"); glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) GetFunction("glBindVertexArray"); glGenBuffers = (PFNGLGENBUFFERSPROC) GetFunction("glGenBuffers"); glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) GetFunction("glDeleteBuffers"); glBindBuffer = (PFNGLBINDBUFFERPROC) GetFunction("glBindBuffer"); glBufferData = (PFNGLBUFFERDATAPROC) GetFunction("glBufferData"); glMapBuffer = (PFNGLMAPBUFFERPROC) GetFunction("glMapBuffer"); glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) GetFunction("glUnmapBuffer"); glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) GetFunction("glDisableVertexAttribArray"); glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) GetFunction("glVertexAttribPointer"); glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) GetFunction("glEnableVertexAttribArray"); glActiveTexture = (PFNGLACTIVETEXTUREPROC) GetFunction("glActiveTexture"); glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) GetFunction("glUniformMatrix3fv"); glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) GetFunction("glUniformMatrix4fv"); glUniform1i = (PFNGLUNIFORM1IPROC) GetFunction("glUniform1i"); glUniform1fv = (PFNGLUNIFORM1FVPROC) GetFunction("glUniform1fv"); glUniform2fv = (PFNGLUNIFORM2FVPROC) GetFunction("glUniform2fv"); glUniform3fv = (PFNGLUNIFORM3FVPROC) GetFunction("glUniform3fv"); glUniform2fv = (PFNGLUNIFORM2FVPROC) GetFunction("glUniform2fv"); glUniform4fv = (PFNGLUNIFORM4FVPROC) GetFunction("glUniform4fv"); glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) GetFunction("glGetUniformLocation"); glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) GetFunction("glGetActiveUniform"); glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) GetFunction("glGetShaderInfoLog"); glGetShaderiv = (PFNGLGETSHADERIVPROC) GetFunction("glGetShaderiv"); glCompileShader = (PFNGLCOMPILESHADERPROC) GetFunction("glCompileShader"); glShaderSource = (PFNGLSHADERSOURCEPROC) GetFunction("glShaderSource"); glCreateShader = (PFNGLCREATESHADERPROC) GetFunction("glCreateShader"); glDeleteShader = (PFNGLDELETESHADERPROC) GetFunction("glDeleteShader"); glCreateProgram = (PFNGLCREATEPROGRAMPROC) GetFunction("glCreateProgram"); glDeleteProgram = (PFNGLDELETEPROGRAMPROC) GetFunction("glDeleteProgram"); glUseProgram = (PFNGLUSEPROGRAMPROC) GetFunction("glUseProgram"); glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) GetFunction("glGetProgramInfoLog"); glGetProgramiv = (PFNGLGETPROGRAMIVPROC) GetFunction("glGetProgramiv"); glLinkProgram = (PFNGLLINKPROGRAMPROC) GetFunction("glLinkProgram"); glAttachShader = (PFNGLATTACHSHADERPROC) GetFunction("glAttachShader"); glDetachShader = (PFNGLDETACHSHADERPROC) GetFunction("glDetachShader"); glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) GetFunction("glBindAttribLocation"); glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) GetFunction("glGetAttribLocation"); } #endif Buffer::Buffer(RenderParams* rp) : pParams(rp), Size(0), Use(0), GLBuffer(0) { } Buffer::~Buffer() { if (GLBuffer) glDeleteBuffers(1, &GLBuffer); } bool Buffer::Data(int use, const void* buffer, size_t size) { Size = size; switch (use & Buffer_TypeMask) { case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break; default: Use = GL_ARRAY_BUFFER; break; } if (!GLBuffer) glGenBuffers(1, &GLBuffer); int mode = GL_DYNAMIC_DRAW; if (use & Buffer_ReadOnly) mode = GL_STATIC_DRAW; glBindBuffer(Use, GLBuffer); glBufferData(Use, size, buffer, mode); return 1; } void* Buffer::Map(size_t, size_t, int) { int mode = GL_WRITE_ONLY; //if (flags & Map_Unsynchronized) // mode |= GL_MAP_UNSYNCHRONIZED; glBindBuffer(Use, GLBuffer); void* v = glMapBuffer(Use, mode); return v; } bool Buffer::Unmap(void*) { glBindBuffer(Use, GLBuffer); int r = glUnmapBuffer(Use); return r != 0; } ShaderSet::ShaderSet() { Prog = glCreateProgram(); } ShaderSet::~ShaderSet() { glDeleteProgram(Prog); } GLint ShaderSet::GetGLShader(Shader* s) { switch (s->Stage) { case Shader_Vertex: { ShaderImpl* gls = (ShaderImpl*)s; return gls->GLShader; } break; case Shader_Fragment: { ShaderImpl* gls = (ShaderImpl*)s; return gls->GLShader; } break; default: break; } return -1; } void ShaderSet::SetShader(Shader *s) { Shaders[s->Stage] = s; GLint GLShader = GetGLShader(s); glAttachShader(Prog, GLShader); if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment]) Link(); } void ShaderSet::UnsetShader(int stage) { if (Shaders[stage] == NULL) return; GLint GLShader = GetGLShader(Shaders[stage]); glDetachShader(Prog, GLShader); Shaders[stage] = NULL; } bool ShaderSet::SetUniform(const char* name, int n, const float* v) { for (unsigned int i = 0; i < UniformInfo.GetSize(); i++) if (!strcmp(UniformInfo[i].Name.ToCStr(), name)) { OVR_ASSERT(UniformInfo[i].Location >= 0); glUseProgram(Prog); switch (UniformInfo[i].Type) { case 1: glUniform1fv(UniformInfo[i].Location, n, v); break; case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break; case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break; case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break; case 12: glUniformMatrix3fv(UniformInfo[i].Location, 1, 1, v); break; case 16: glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, v); break; default: OVR_ASSERT(0); } return 1; } OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name)); return 0; } bool ShaderSet::Link() { glLinkProgram(Prog); GLint r; glGetProgramiv(Prog, GL_LINK_STATUS, &r); if (!r) { GLchar msg[1024]; glGetProgramInfoLog(Prog, sizeof(msg), 0, msg); OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg)); if (!r) return 0; } glUseProgram(Prog); UniformInfo.Clear(); LightingVer = 0; UsesLighting = 0; GLint uniformCount = 0; glGetProgramiv(Prog, GL_ACTIVE_UNIFORMS, &uniformCount); OVR_ASSERT(uniformCount >= 0); for(GLuint i = 0; i < (GLuint)uniformCount; i++) { GLsizei namelen; GLint size = 0; GLenum type; GLchar name[32]; glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name); if (size) { int l = glGetUniformLocation(Prog, name); char *np = name; while (*np) { if (*np == '[') *np = 0; np++; } Uniform u; u.Name = name; u.Location = l; u.Size = size; switch (type) { case GL_FLOAT: u.Type = 1; break; case GL_FLOAT_VEC2: u.Type = 2; break; case GL_FLOAT_VEC3: u.Type = 3; break; case GL_FLOAT_VEC4: u.Type = 4; break; case GL_FLOAT_MAT3: u.Type = 12; break; case GL_FLOAT_MAT4: u.Type = 16; break; default: continue; } UniformInfo.PushBack(u); if (!strcmp(name, "LightCount")) UsesLighting = 1; } else break; } ProjLoc = glGetUniformLocation(Prog, "Proj"); ViewLoc = glGetUniformLocation(Prog, "View"); for (int i = 0; i < 8; i++) { char texv[32]; OVR_sprintf(texv, 10, "Texture%d", i); TexLoc[i] = glGetUniformLocation(Prog, texv); if (TexLoc[i] < 0) break; glUniform1i(TexLoc[i], i); } if (UsesLighting) OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0); return 1; } bool ShaderBase::SetUniform(const char* name, int n, const float* v) { for(unsigned i = 0; i < UniformReflSize; i++) { if (!strcmp(UniformRefl[i].Name, name)) { memcpy(UniformData + UniformRefl[i].Offset, v, n * sizeof(float)); return 1; } } return 0; } bool ShaderBase::SetUniformBool(const char* name, int n, const bool* v) { OVR_UNUSED(n); for(unsigned i = 0; i < UniformReflSize; i++) { if (!strcmp(UniformRefl[i].Name, name)) { memcpy(UniformData + UniformRefl[i].Offset, v, UniformRefl[i].Size); return 1; } } return 0; } void ShaderBase::InitUniforms(const Uniform* refl, size_t reflSize) { if(!refl) { UniformRefl = NULL; UniformReflSize = 0; UniformsSize = 0; if (UniformData) { OVR_FREE(UniformData); UniformData = 0; } return; // no reflection data } UniformRefl = refl; UniformReflSize = reflSize; UniformsSize = UniformRefl[UniformReflSize-1].Offset + UniformRefl[UniformReflSize-1].Size; UniformData = (unsigned char*)OVR_ALLOC(UniformsSize); } Texture::Texture(RenderParams* rp, int w, int h) : IsUserAllocated(true), pParams(rp), TexId(0), Width(w), Height(h) { if (w && h) glGenTextures(1, &TexId); } Texture::~Texture() { if (TexId && !IsUserAllocated) glDeleteTextures(1, &TexId); } void Texture::Set(int slot, ShaderStage) const { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(GL_TEXTURE_2D, TexId); } void Texture::SetSampleMode(int sm) { glBindTexture(GL_TEXTURE_2D, TexId); switch (sm & Sample_FilterMask) { case Sample_Linear: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); break; case Sample_Anisotropic: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); break; case Sample_Nearest: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1); break; } switch (sm & Sample_AddressMask) { case Sample_Repeat: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); break; case Sample_Clamp: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); break; case Sample_ClampBorder: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); break; } } void Texture::UpdatePlaceholderTexture(GLuint texId, const Sizei& textureSize) { if (!IsUserAllocated && TexId && texId != TexId) glDeleteTextures(1, &TexId); TexId = texId; Width = textureSize.w; Height = textureSize.h; IsUserAllocated = true; } }}} \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h new file mode 100644 index 0000000..9de3f83 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/CAPI/GL/CAPI_GL_Util.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : CAPI_GL_Util.h Content : Utility header for OpenGL Created : March 27, 2014 Authors : Andrew Reisse, David Borel Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software 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. ************************************************************************************/ #ifndef INC_OVR_CAPI_GL_Util_h #define INC_OVR_CAPI_GL_Util_h #include "../../OVR_CAPI.h" #include "../../Kernel/OVR_Array.h" #include "../../Kernel/OVR_Math.h" #include "../../Kernel/OVR_RefCount.h" #include "../../Kernel/OVR_String.h" #include "../../Kernel/OVR_Types.h" #include "../../Kernel/OVR_Log.h" #if defined(OVR_OS_WIN32) #include #endif #if defined(OVR_OS_MAC) #include #include #else #ifndef GL_GLEXT_PROTOTYPES #define GL_GLEXT_PROTOTYPES #endif #include #include #if defined(OVR_OS_WIN32) #include #elif defined(OVR_OS_LINUX) #include #endif #endif namespace OVR { namespace CAPI { namespace GL { // GL extension Hooks for Non-Mac. #if !defined(OVR_OS_MAC) // Let Windows apps build without linking GL. #if defined(OVR_OS_WIN32) typedef void (__stdcall *PFNGLENABLEPROC) (GLenum); typedef void (__stdcall *PFNGLDISABLEPROC) (GLenum); typedef void (__stdcall *PFNGLGETFLOATVPROC) (GLenum, GLfloat*); typedef const GLubyte * (__stdcall *PFNGLGETSTRINGPROC) (GLenum); typedef void (__stdcall *PFNGLGETINTEGERVPROC) (GLenum, GLint*); typedef PROC (__stdcall *PFNWGLGETPROCADDRESS) (LPCSTR); typedef void (__stdcall *PFNGLFLUSHPROC) (); typedef void (__stdcall *PFNGLFINISHPROC) (); typedef void (__stdcall *PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); typedef void (__stdcall *PFNGLCLEARPROC) (GLbitfield); typedef void (__stdcall *PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); typedef void (__stdcall *PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); typedef void (__stdcall *PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); typedef void (__stdcall *PFNGLDELETETEXTURESPROC) (GLsizei n, GLuint *textures); typedef void (__stdcall *PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); typedef void (__stdcall *PFNGLCLEARCOLORPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a); typedef void (__stdcall *PFNGLCLEARDEPTHPROC) (GLclampd depth); typedef void (__stdcall *PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); typedef void (__stdcall *PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); extern PFNWGLGETPROCADDRESS wglGetProcAddress; extern PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; extern PFNGLENABLEPROC glEnable; extern PFNGLDISABLEPROC glDisable; extern PFNGLCOLORMASKPROC glColorMask; extern PFNGLGETFLOATVPROC glGetFloatv; extern PFNGLGETSTRINGPROC glGetString; extern PFNGLGETINTEGERVPROC glGetIntegerv; extern PFNGLCLEARPROC glClear; extern PFNGLCLEARCOLORPROC glClearColor; extern PFNGLCLEARDEPTHPROC glClearDepth; extern PFNGLVIEWPORTPROC glViewport; extern PFNGLDRAWARRAYSPROC glDrawArrays; extern PFNGLDRAWELEMENTSPROC glDrawElements; extern PFNGLGENTEXTURESPROC glGenTextures; extern PFNGLDELETETEXTURESPROC glDeleteTextures; extern PFNGLBINDTEXTUREPROC glBindTexture; extern PFNGLTEXPARAMETERIPROC glTexParameteri; extern PFNGLFLUSHPROC glFlush; extern PFNGLFINISHPROC glFinish; #elif defined(OVR_OS_LINUX) extern PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT; #endif // defined(OVR_OS_WIN32) extern PFNGLDELETESHADERPROC glDeleteShader; extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; extern PFNGLACTIVETEXTUREPROC glActiveTexture; extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; extern PFNGLBINDBUFFERPROC glBindBuffer; extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; extern PFNGLDELETEBUFFERSPROC glDeleteBuffers; extern PFNGLBUFFERDATAPROC glBufferData; extern PFNGLGENBUFFERSPROC glGenBuffers; extern PFNGLMAPBUFFERPROC glMapBuffer; extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; extern PFNGLGETSHADERIVPROC glGetShaderiv; extern PFNGLCOMPILESHADERPROC glCompileShader; extern PFNGLSHADERSOURCEPROC glShaderSource; extern PFNGLCREATESHADERPROC glCreateShader; extern PFNGLCREATEPROGRAMPROC glCreateProgram; extern PFNGLATTACHSHADERPROC glAttachShader; extern PFNGLDETACHSHADERPROC glDetachShader; extern PFNGLDELETEPROGRAMPROC glDeleteProgram; extern PFNGLUNIFORM1IPROC glUniform1i; extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform; extern PFNGLUSEPROGRAMPROC glUseProgram; extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; extern PFNGLGETPROGRAMIVPROC glGetProgramiv; extern PFNGLLINKPROGRAMPROC glLinkProgram; extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; extern PFNGLUNIFORM4FVPROC glUniform4fv; extern PFNGLUNIFORM3FVPROC glUniform3fv; extern PFNGLUNIFORM2FVPROC glUniform2fv; extern PFNGLUNIFORM1FVPROC glUniform1fv; extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; extern void InitGLExtensions(); #endif // !defined(OVR_OS_MAC) // Rendering primitive type used to render Model. enum PrimitiveType { Prim_Triangles, Prim_Lines, Prim_TriangleStrip, Prim_Unknown, Prim_Count }; // Types of shaders that can be stored together in a ShaderSet. enum ShaderStage { Shader_Vertex = 0, Shader_Fragment = 2, Shader_Pixel = 2, Shader_Count = 3, }; enum MapFlags { Map_Discard = 1, Map_Read = 2, // do not use Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE }; // Buffer types used for uploading geometry & constants. enum BufferUsage { Buffer_Unknown = 0, Buffer_Vertex = 1, Buffer_Index = 2, Buffer_Uniform = 4, Buffer_TypeMask = 0xff, Buffer_ReadOnly = 0x100, // Buffer must be created with Data(). }; enum TextureFormat { Texture_RGBA = 0x0100, Texture_Depth = 0x8000, Texture_TypeMask = 0xff00, Texture_SamplesMask = 0x00ff, Texture_RenderTarget = 0x10000, Texture_GenMipmaps = 0x20000, }; // Texture sampling modes. enum SampleMode { Sample_Linear = 0, Sample_Nearest = 1, Sample_Anisotropic = 2, Sample_FilterMask = 3, Sample_Repeat = 0, Sample_Clamp = 4, Sample_ClampBorder = 8, // If unsupported Clamp is used instead. Sample_AddressMask =12, Sample_Count =13, }; // Rendering parameters/pointers describing GL rendering setup. struct RenderParams { #if defined(OVR_OS_WIN32) HWND Window; #elif defined(OVR_OS_LINUX) Display* Disp; Window Win; #endif ovrSizei RTSize; int Multisample; }; class Buffer : public RefCountBase { public: RenderParams* pParams; size_t Size; GLenum Use; GLuint GLBuffer; public: Buffer(RenderParams* r); ~Buffer(); GLuint GetBuffer() { return GLBuffer; } virtual size_t GetSize() { return Size; } virtual void* Map(size_t start, size_t size, int flags = 0); virtual bool Unmap(void *m); virtual bool Data(int use, const void* buffer, size_t size); }; class Texture : public RefCountBase { bool IsUserAllocated; public: RenderParams* pParams; GLuint TexId; int Width, Height; Texture(RenderParams* rp, int w, int h); ~Texture(); virtual int GetWidth() const { return Width; } virtual int GetHeight() const { return Height; } virtual void SetSampleMode(int sm); // Updates texture to point to specified resources // - used for slave rendering. void UpdatePlaceholderTexture(GLuint texId, const Sizei& textureSize); virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const; }; // Base class for vertex and pixel shaders. Stored in ShaderSet. class Shader : public RefCountBase { friend class ShaderSet; protected: ShaderStage Stage; public: Shader(ShaderStage s) : Stage(s) {} virtual ~Shader() {} ShaderStage GetStage() const { return Stage; } virtual void Set(PrimitiveType) const { } virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); } protected: virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; } virtual bool SetUniformBool(const char* name, int n, const bool* v) { OVR_UNUSED3(name, n, v); return false; } }; // A group of shaders, one per stage. // A ShaderSet is applied for rendering with a given fill. class ShaderSet : public RefCountBase { protected: Ptr Shaders[Shader_Count]; struct Uniform { String Name; int Location, Size; int Type; // currently number of floats in vector }; Array UniformInfo; public: GLuint Prog; GLint ProjLoc, ViewLoc; GLint TexLoc[8]; bool UsesLighting; int LightingVer; ShaderSet(); ~ShaderSet(); virtual void SetShader(Shader *s); virtual void UnsetShader(int stage); Shader* GetShader(int stage) { return Shaders[stage]; } virtual void Set(PrimitiveType prim) const { glUseProgram(Prog); for (int i = 0; i < Shader_Count; i++) if (Shaders[i]) Shaders[i]->Set(prim); } // Set a uniform (other than the standard matrices). It is undefined whether the // uniforms from one shader occupy the same space as those in other shaders // (unless a buffer is used, then each buffer is independent). virtual bool SetUniform(const char* name, int n, const float* v); bool SetUniform1f(const char* name, float x) { const float v[] = {x}; return SetUniform(name, 1, v); } bool SetUniform2f(const char* name, float x, float y) { const float v[] = {x,y}; return SetUniform(name, 2, v); } bool SetUniform3f(const char* name, float x, float y, float z) { const float v[] = {x,y,z}; return SetUniform(name, 3, v); } bool SetUniform4f(const char* name, float x, float y, float z, float w = 1) { const float v[] = {x,y,z,w}; return SetUniform(name, 4, v); } bool SetUniformv(const char* name, const Vector3f& v) { const float a[] = {v.x,v.y,v.z,1}; return SetUniform(name, 4, a); } virtual bool SetUniform4x4f(const char* name, const Matrix4f& m) { Matrix4f mt = m.Transposed(); return SetUniform(name, 16, &mt.M[0][0]); } protected: GLint GetGLShader(Shader* s); bool Link(); }; // Fill combines a ShaderSet (vertex, pixel) with textures, if any. // Every model has a fill. class ShaderFill : public RefCountBase { Ptr Shaders; Ptr Textures[8]; void* InputLayout; // HACK this should be abstracted public: ShaderFill(ShaderSet* sh) : Shaders(sh) { InputLayout = NULL; } ShaderFill(ShaderSet& sh) : Shaders(sh) { InputLayout = NULL; } ShaderSet* GetShaders() const { return Shaders; } void* GetInputLayout() const { return InputLayout; } virtual void Set(PrimitiveType prim = Prim_Unknown) const { Shaders->Set(prim); for(int i = 0; i < 8; i++) { if(Textures[i]) { Textures[i]->Set(i); } } } virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; } }; struct DisplayId { // Windows String MonitorName; // Monitor name for fullscreen mode // MacOS long CgDisplayId; // CGDirectDisplayID DisplayId() : CgDisplayId(0) {} DisplayId(long id) : CgDisplayId(id) {} DisplayId(String m, long id=0) : MonitorName(m), CgDisplayId(id) {} operator bool () const { return MonitorName.GetLength() || CgDisplayId; } bool operator== (const DisplayId& b) const { return CgDisplayId == b.CgDisplayId && (strstr(MonitorName.ToCStr(), b.MonitorName.ToCStr()) || strstr(b.MonitorName.ToCStr(), MonitorName.ToCStr())); } }; class ShaderBase : public Shader { public: RenderParams* pParams; unsigned char* UniformData; int UniformsSize; enum VarType { VARTYPE_FLOAT, VARTYPE_INT, VARTYPE_BOOL, }; struct Uniform { const char* Name; VarType Type; int Offset, Size; }; const Uniform* UniformRefl; size_t UniformReflSize; ShaderBase(RenderParams* rp, ShaderStage stage) : Shader(stage), pParams(rp), UniformData(0), UniformsSize(0) {} ~ShaderBase() { if (UniformData) OVR_FREE(UniformData); } void InitUniforms(const Uniform* refl, size_t reflSize); bool SetUniform(const char* name, int n, const float* v); bool SetUniformBool(const char* name, int n, const bool* v); }; template class ShaderImpl : public ShaderBase { friend class ShaderSet; public: ShaderImpl(RenderParams* rp, void* s, size_t size, const Uniform* refl, size_t reflSize) : ShaderBase(rp, SStage) , GLShader(0) { bool success; OVR_UNUSED(size); success = Compile((const char*) s); OVR_ASSERT(success); InitUniforms(refl, reflSize); } ~ShaderImpl() { if (GLShader) { glDeleteShader(GLShader); GLShader = 0; } } bool Compile(const char* src) { if (!GLShader) GLShader = glCreateShader(GLStage()); glShaderSource(GLShader, 1, &src, 0); glCompileShader(GLShader); GLint r; glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r); if (!r) { GLchar msg[1024]; glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg); if (msg[0]) OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg)); return 0; } return 1; } GLenum GLStage() const { return SType; } private: GLuint GLShader; }; typedef ShaderImpl VertexShader; typedef ShaderImpl FragmentShader; }}} #endif // INC_OVR_CAPI_GL_Util_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Alg.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Alg.cpp new file mode 100644 index 0000000..2ac12fb --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Alg.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Alg.cpp Content : Static lookup tables for Alg functions Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Types.h" namespace OVR { namespace Alg { //------------------------------------------------------------------------ extern const UByte UpperBitTable[256] = { 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; extern const UByte LowerBitTable[256] = { 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0, 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 }; }} // OVE::Alg \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Alg.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Alg.h new file mode 100644 index 0000000..fb2f099 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Alg.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Alg.h Content : Simple general purpose algorithms: Sort, Binary Search, etc. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Alg_h #define OVR_Alg_h #include "OVR_Types.h" #include namespace OVR { namespace Alg { //----------------------------------------------------------------------------------- // ***** Operator extensions template OVR_FORCE_INLINE void Swap(T &a, T &b) { T temp(a); a = b; b = temp; } // ***** min/max are not implemented in Visual Studio 6 standard STL template OVR_FORCE_INLINE const T Min(const T a, const T b) { return (a < b) ? a : b; } template OVR_FORCE_INLINE const T Max(const T a, const T b) { return (b < a) ? a : b; } template OVR_FORCE_INLINE const T Clamp(const T v, const T minVal, const T maxVal) { return Max(minVal, Min(v, maxVal)); } template OVR_FORCE_INLINE int Chop(T f) { return (int)f; } template OVR_FORCE_INLINE T Lerp(T a, T b, T f) { return (b - a) * f + a; } // These functions stand to fix a stupid VC++ warning (with /Wp64 on): // "warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned', possible loss of data" // Use these functions instead of gmin/gmax if the argument has size // of the pointer to avoid the warning. Though, functionally they are // absolutelly the same as regular gmin/gmax. template OVR_FORCE_INLINE const T PMin(const T a, const T b) { OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); return (a < b) ? a : b; } template OVR_FORCE_INLINE const T PMax(const T a, const T b) { OVR_COMPILER_ASSERT(sizeof(T) == sizeof(UPInt)); return (b < a) ? a : b; } template OVR_FORCE_INLINE const T Abs(const T v) { return (v>=0) ? v : -v; } //----------------------------------------------------------------------------------- // ***** OperatorLess // template struct OperatorLess { static bool Compare(const T& a, const T& b) { return a < b; } }; //----------------------------------------------------------------------------------- // ***** QuickSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The comparison predicate must be specified. template void QuickSortSliced(Array& arr, UPInt start, UPInt end, Less less) { enum { Threshold = 9 }; if(end - start < 2) return; SPInt stack[80]; SPInt* top = stack; SPInt base = (SPInt)start; SPInt limit = (SPInt)end; for(;;) { SPInt len = limit - base; SPInt i, j, pivot; if(len > Threshold) { // we use base + len/2 as the pivot pivot = base + len / 2; Swap(arr[base], arr[pivot]); i = base + 1; j = limit - 1; // now ensure that *i <= *base <= *j if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); for(;;) { do i++; while( less(arr[i], arr[base]) ); do j--; while( less(arr[base], arr[j]) ); if( i > j ) { break; } Swap(arr[i], arr[j]); } Swap(arr[base], arr[j]); // now, push the largest sub-array if(j - base > limit - i) { top[0] = base; top[1] = j; base = i; } else { top[0] = i; top[1] = limit; limit = j; } top += 2; } else { // the sub-array is small, perform insertion sort j = base; i = j + 1; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j == base) { break; } } } if(top > stack) { top -= 2; base = top[0]; limit = top[1]; } else { break; } } } } //----------------------------------------------------------------------------------- // ***** QuickSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The data type must have a defined "<" operator. template void QuickSortSliced(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; QuickSortSliced(arr, start, end, OperatorLess::Compare); } // Same as corresponding G_QuickSortSliced but with checking array limits to avoid // crash in the case of wrong comparator functor. template bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end, Less less) { enum { Threshold = 9 }; if(end - start < 2) return true; SPInt stack[80]; SPInt* top = stack; SPInt base = (SPInt)start; SPInt limit = (SPInt)end; for(;;) { SPInt len = limit - base; SPInt i, j, pivot; if(len > Threshold) { // we use base + len/2 as the pivot pivot = base + len / 2; Swap(arr[base], arr[pivot]); i = base + 1; j = limit - 1; // now ensure that *i <= *base <= *j if(less(arr[j], arr[i])) Swap(arr[j], arr[i]); if(less(arr[base], arr[i])) Swap(arr[base], arr[i]); if(less(arr[j], arr[base])) Swap(arr[j], arr[base]); for(;;) { do { i++; if (i >= limit) return false; } while( less(arr[i], arr[base]) ); do { j--; if (j < 0) return false; } while( less(arr[base], arr[j]) ); if( i > j ) { break; } Swap(arr[i], arr[j]); } Swap(arr[base], arr[j]); // now, push the largest sub-array if(j - base > limit - i) { top[0] = base; top[1] = j; base = i; } else { top[0] = i; top[1] = limit; limit = j; } top += 2; } else { // the sub-array is small, perform insertion sort j = base; i = j + 1; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j == base) { break; } } } if(top > stack) { top -= 2; base = top[0]; limit = top[1]; } else { break; } } } return true; } template bool QuickSortSlicedSafe(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; return QuickSortSlicedSafe(arr, start, end, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** QuickSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The comparison predicate must be specified. template void QuickSort(Array& arr, Less less) { QuickSortSliced(arr, 0, arr.GetSize(), less); } // checks for boundaries template bool QuickSortSafe(Array& arr, Less less) { return QuickSortSlicedSafe(arr, 0, arr.GetSize(), less); } //----------------------------------------------------------------------------------- // ***** QuickSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The data type must have a defined "<" operator. template void QuickSort(Array& arr) { typedef typename Array::ValueType ValueType; QuickSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); } template bool QuickSortSafe(Array& arr) { typedef typename Array::ValueType ValueType; return QuickSortSlicedSafe(arr, 0, arr.GetSize(), OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** InsertionSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The comparison predicate must be specified. // Unlike Quick Sort, the Insertion Sort works much slower in average, // but may be much faster on almost sorted arrays. Besides, it guarantees // that the elements will not be swapped if not necessary. For example, // an array with all equal elements will remain "untouched", while // Quick Sort will considerably shuffle the elements in this case. template void InsertionSortSliced(Array& arr, UPInt start, UPInt end, Less less) { UPInt j = start; UPInt i = j + 1; UPInt limit = end; for(; i < limit; j = i, i++) { for(; less(arr[j + 1], arr[j]); j--) { Swap(arr[j + 1], arr[j]); if(j <= start) { break; } } } } //----------------------------------------------------------------------------------- // ***** InsertionSortSliced // // Sort any part of any array: plain, Array, ArrayPaged, ArrayUnsafe. // The range is specified with start, end, where "end" is exclusive! // The data type must have a defined "<" operator. template void InsertionSortSliced(Array& arr, UPInt start, UPInt end) { typedef typename Array::ValueType ValueType; InsertionSortSliced(arr, start, end, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** InsertionSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The comparison predicate must be specified. template void InsertionSort(Array& arr, Less less) { InsertionSortSliced(arr, 0, arr.GetSize(), less); } //----------------------------------------------------------------------------------- // ***** InsertionSort // // Sort an array Array, ArrayPaged, ArrayUnsafe. // The array must have GetSize() function. // The data type must have a defined "<" operator. template void InsertionSort(Array& arr) { typedef typename Array::ValueType ValueType; InsertionSortSliced(arr, 0, arr.GetSize(), OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** Median // Returns a median value of the input array. // Caveats: partially sorts the array, returns a reference to the array element // TBD: This needs to be optimized and generalized // template typename Array::ValueType& Median(Array& arr) { UPInt count = arr.GetSize(); UPInt mid = (count - 1) / 2; OVR_ASSERT(count > 0); for (UPInt j = 0; j <= mid; j++) { UPInt min = j; for (UPInt k = j + 1; k < count; k++) if (arr[k] < arr[min]) min = k; Swap(arr[j], arr[min]); } return arr[mid]; } //----------------------------------------------------------------------------------- // ***** LowerBoundSliced // template UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) { SPInt first = (SPInt)start; SPInt len = (SPInt)(end - start); SPInt half; SPInt middle; while(len > 0) { half = len >> 1; middle = first + half; if(less(arr[middle], val)) { first = middle + 1; len = len - half - 1; } else { len = half; } } return (UPInt)first; } //----------------------------------------------------------------------------------- // ***** LowerBoundSliced // template UPInt LowerBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) { return LowerBoundSliced(arr, start, end, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBoundSized // template UPInt LowerBoundSized(const Array& arr, UPInt size, const Value& val) { return LowerBoundSliced(arr, 0, size, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** LowerBound // template UPInt LowerBound(const Array& arr, const Value& val, Less less) { return LowerBoundSliced(arr, 0, arr.GetSize(), val, less); } //----------------------------------------------------------------------------------- // ***** LowerBound // template UPInt LowerBound(const Array& arr, const Value& val) { return LowerBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBoundSliced // template UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val, Less less) { SPInt first = (SPInt)start; SPInt len = (SPInt)(end - start); SPInt half; SPInt middle; while(len > 0) { half = len >> 1; middle = first + half; if(less(val, arr[middle])) { len = half; } else { first = middle + 1; len = len - half - 1; } } return (UPInt)first; } //----------------------------------------------------------------------------------- // ***** UpperBoundSliced // template UPInt UpperBoundSliced(const Array& arr, UPInt start, UPInt end, const Value& val) { return UpperBoundSliced(arr, start, end, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBoundSized // template UPInt UpperBoundSized(const Array& arr, UPInt size, const Value& val) { return UpperBoundSliced(arr, 0, size, val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** UpperBound // template UPInt UpperBound(const Array& arr, const Value& val, Less less) { return UpperBoundSliced(arr, 0, arr.GetSize(), val, less); } //----------------------------------------------------------------------------------- // ***** UpperBound // template UPInt UpperBound(const Array& arr, const Value& val) { return UpperBoundSliced(arr, 0, arr.GetSize(), val, OperatorLess::Compare); } //----------------------------------------------------------------------------------- // ***** ReverseArray // template void ReverseArray(Array& arr) { SPInt from = 0; SPInt to = arr.GetSize() - 1; while(from < to) { Swap(arr[from], arr[to]); ++from; --to; } } // ***** AppendArray // template void AppendArray(CDst& dst, const CSrc& src) { UPInt i; for(i = 0; i < src.GetSize(); i++) dst.PushBack(src[i]); } //----------------------------------------------------------------------------------- // ***** ArrayAdaptor // // A simple adapter that provides the GetSize() method and overloads // operator []. Used to wrap plain arrays in QuickSort and such. template class ArrayAdaptor { public: typedef T ValueType; ArrayAdaptor() : Data(0), Size(0) {} ArrayAdaptor(T* ptr, UPInt size) : Data(ptr), Size(size) {} UPInt GetSize() const { return Size; } const T& operator [] (UPInt i) const { return Data[i]; } T& operator [] (UPInt i) { return Data[i]; } private: T* Data; UPInt Size; }; //----------------------------------------------------------------------------------- // ***** GConstArrayAdaptor // // A simple const adapter that provides the GetSize() method and overloads // operator []. Used to wrap plain arrays in LowerBound and such. template class ConstArrayAdaptor { public: typedef T ValueType; ConstArrayAdaptor() : Data(0), Size(0) {} ConstArrayAdaptor(const T* ptr, UPInt size) : Data(ptr), Size(size) {} UPInt GetSize() const { return Size; } const T& operator [] (UPInt i) const { return Data[i]; } private: const T* Data; UPInt Size; }; //----------------------------------------------------------------------------------- extern const UByte UpperBitTable[256]; extern const UByte LowerBitTable[256]; //----------------------------------------------------------------------------------- inline UByte UpperBit(UPInt val) { #ifndef OVR_64BIT_POINTERS if (val & 0xFFFF0000) { return (val & 0xFF000000) ? UpperBitTable[(val >> 24) ] + 24: UpperBitTable[(val >> 16) & 0xFF] + 16; } return (val & 0xFF00) ? UpperBitTable[(val >> 8) & 0xFF] + 8: UpperBitTable[(val ) & 0xFF]; #else if (val & 0xFFFFFFFF00000000) { if (val & 0xFFFF000000000000) { return (val & 0xFF00000000000000) ? UpperBitTable[(val >> 56) ] + 56: UpperBitTable[(val >> 48) & 0xFF] + 48; } return (val & 0xFF0000000000) ? UpperBitTable[(val >> 40) & 0xFF] + 40: UpperBitTable[(val >> 32) & 0xFF] + 32; } else { if (val & 0xFFFF0000) { return (val & 0xFF000000) ? UpperBitTable[(val >> 24) ] + 24: UpperBitTable[(val >> 16) & 0xFF] + 16; } return (val & 0xFF00) ? UpperBitTable[(val >> 8) & 0xFF] + 8: UpperBitTable[(val ) & 0xFF]; } #endif } //----------------------------------------------------------------------------------- inline UByte LowerBit(UPInt val) { #ifndef OVR_64BIT_POINTERS if (val & 0xFFFF) { return (val & 0xFF) ? LowerBitTable[ val & 0xFF]: LowerBitTable[(val >> 8) & 0xFF] + 8; } return (val & 0xFF0000) ? LowerBitTable[(val >> 16) & 0xFF] + 16: LowerBitTable[(val >> 24) & 0xFF] + 24; #else if (val & 0xFFFFFFFF) { if (val & 0xFFFF) { return (val & 0xFF) ? LowerBitTable[ val & 0xFF]: LowerBitTable[(val >> 8) & 0xFF] + 8; } return (val & 0xFF0000) ? LowerBitTable[(val >> 16) & 0xFF] + 16: LowerBitTable[(val >> 24) & 0xFF] + 24; } else { if (val & 0xFFFF00000000) { return (val & 0xFF00000000) ? LowerBitTable[(val >> 32) & 0xFF] + 32: LowerBitTable[(val >> 40) & 0xFF] + 40; } return (val & 0xFF000000000000) ? LowerBitTable[(val >> 48) & 0xFF] + 48: LowerBitTable[(val >> 56) & 0xFF] + 56; } #endif } // ******* Special (optimized) memory routines // Note: null (bad) pointer is not tested class MemUtil { public: // Memory compare static int Cmp (const void* p1, const void* p2, UPInt byteCount) { return memcmp(p1, p2, byteCount); } static int Cmp16(const void* p1, const void* p2, UPInt int16Count); static int Cmp32(const void* p1, const void* p2, UPInt int32Count); static int Cmp64(const void* p1, const void* p2, UPInt int64Count); }; // ** Inline Implementation inline int MemUtil::Cmp16(const void* p1, const void* p2, UPInt int16Count) { SInt16* pa = (SInt16*)p1; SInt16* pb = (SInt16*)p2; unsigned ic = 0; if (int16Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int16Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } inline int MemUtil::Cmp32(const void* p1, const void* p2, UPInt int32Count) { SInt32* pa = (SInt32*)p1; SInt32* pb = (SInt32*)p2; unsigned ic = 0; if (int32Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int32Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } inline int MemUtil::Cmp64(const void* p1, const void* p2, UPInt int64Count) { SInt64* pa = (SInt64*)p1; SInt64* pb = (SInt64*)p2; unsigned ic = 0; if (int64Count == 0) return 0; while (pa[ic] == pb[ic]) if (++ic==int64Count) return 0; return pa[ic] > pb[ic] ? 1 : -1; } // ** End Inline Implementation //----------------------------------------------------------------------------------- // ******* Byte Order Conversions namespace ByteUtil { // *** Swap Byte Order // Swap the byte order of a byte array inline void SwapOrder(void* pv, int size) { UByte* pb = (UByte*)pv; UByte temp; for (int i = 0; i < size>>1; i++) { temp = pb[size-1-i]; pb[size-1-i] = pb[i]; pb[i] = temp; } } // Swap the byte order of primitive types inline UByte SwapOrder(UByte v) { return v; } inline SByte SwapOrder(SByte v) { return v; } inline UInt16 SwapOrder(UInt16 v) { return UInt16(v>>8)|UInt16(v<<8); } inline SInt16 SwapOrder(SInt16 v) { return SInt16((UInt16(v)>>8)|(v<<8)); } inline UInt32 SwapOrder(UInt32 v) { return (v>>24)|((v&0x00FF0000)>>8)|((v&0x0000FF00)<<8)|(v<<24); } inline SInt32 SwapOrder(SInt32 p) { return (SInt32)SwapOrder(UInt32(p)); } inline UInt64 SwapOrder(UInt64 v) { return (v>>56) | ((v&UInt64(0x00FF000000000000ULL))>>40) | ((v&UInt64(0x0000FF0000000000ULL))>>24) | ((v&UInt64(0x000000FF00000000ULL))>>8) | ((v&UInt64(0x00000000FF000000ULL))<<8) | ((v&UInt64(0x0000000000FF0000ULL))<<24) | ((v&UInt64(0x000000000000FF00ULL))<<40) | (v<<56); } inline SInt64 SwapOrder(SInt64 v) { return (SInt64)SwapOrder(UInt64(v)); } inline float SwapOrder(float p) { union { float p; UInt32 v; } u; u.p = p; u.v = SwapOrder(u.v); return u.p; } inline double SwapOrder(double p) { union { double p; UInt64 v; } u; u.p = p; u.v = SwapOrder(u.v); return u.p; } // *** Byte-order conversion #if (OVR_BYTE_ORDER == OVR_LITTLE_ENDIAN) // Little Endian to System (LE) inline UByte LEToSystem(UByte v) { return v; } inline SByte LEToSystem(SByte v) { return v; } inline UInt16 LEToSystem(UInt16 v) { return v; } inline SInt16 LEToSystem(SInt16 v) { return v; } inline UInt32 LEToSystem(UInt32 v) { return v; } inline SInt32 LEToSystem(SInt32 v) { return v; } inline UInt64 LEToSystem(UInt64 v) { return v; } inline SInt64 LEToSystem(SInt64 v) { return v; } inline float LEToSystem(float v) { return v; } inline double LEToSystem(double v) { return v; } // Big Endian to System (LE) inline UByte BEToSystem(UByte v) { return SwapOrder(v); } inline SByte BEToSystem(SByte v) { return SwapOrder(v); } inline UInt16 BEToSystem(UInt16 v) { return SwapOrder(v); } inline SInt16 BEToSystem(SInt16 v) { return SwapOrder(v); } inline UInt32 BEToSystem(UInt32 v) { return SwapOrder(v); } inline SInt32 BEToSystem(SInt32 v) { return SwapOrder(v); } inline UInt64 BEToSystem(UInt64 v) { return SwapOrder(v); } inline SInt64 BEToSystem(SInt64 v) { return SwapOrder(v); } inline float BEToSystem(float v) { return SwapOrder(v); } inline double BEToSystem(double v) { return SwapOrder(v); } // System (LE) to Little Endian inline UByte SystemToLE(UByte v) { return v; } inline SByte SystemToLE(SByte v) { return v; } inline UInt16 SystemToLE(UInt16 v) { return v; } inline SInt16 SystemToLE(SInt16 v) { return v; } inline UInt32 SystemToLE(UInt32 v) { return v; } inline SInt32 SystemToLE(SInt32 v) { return v; } inline UInt64 SystemToLE(UInt64 v) { return v; } inline SInt64 SystemToLE(SInt64 v) { return v; } inline float SystemToLE(float v) { return v; } inline double SystemToLE(double v) { return v; } // System (LE) to Big Endian inline UByte SystemToBE(UByte v) { return SwapOrder(v); } inline SByte SystemToBE(SByte v) { return SwapOrder(v); } inline UInt16 SystemToBE(UInt16 v) { return SwapOrder(v); } inline SInt16 SystemToBE(SInt16 v) { return SwapOrder(v); } inline UInt32 SystemToBE(UInt32 v) { return SwapOrder(v); } inline SInt32 SystemToBE(SInt32 v) { return SwapOrder(v); } inline UInt64 SystemToBE(UInt64 v) { return SwapOrder(v); } inline SInt64 SystemToBE(SInt64 v) { return SwapOrder(v); } inline float SystemToBE(float v) { return SwapOrder(v); } inline double SystemToBE(double v) { return SwapOrder(v); } #elif (OVR_BYTE_ORDER == OVR_BIG_ENDIAN) // Little Endian to System (BE) inline UByte LEToSystem(UByte v) { return SwapOrder(v); } inline SByte LEToSystem(SByte v) { return SwapOrder(v); } inline UInt16 LEToSystem(UInt16 v) { return SwapOrder(v); } inline SInt16 LEToSystem(SInt16 v) { return SwapOrder(v); } inline UInt32 LEToSystem(UInt32 v) { return SwapOrder(v); } inline SInt32 LEToSystem(SInt32 v) { return SwapOrder(v); } inline UInt64 LEToSystem(UInt64 v) { return SwapOrder(v); } inline SInt64 LEToSystem(SInt64 v) { return SwapOrder(v); } inline float LEToSystem(float v) { return SwapOrder(v); } inline double LEToSystem(double v) { return SwapOrder(v); } // Big Endian to System (BE) inline UByte BEToSystem(UByte v) { return v; } inline SByte BEToSystem(SByte v) { return v; } inline UInt16 BEToSystem(UInt16 v) { return v; } inline SInt16 BEToSystem(SInt16 v) { return v; } inline UInt32 BEToSystem(UInt32 v) { return v; } inline SInt32 BEToSystem(SInt32 v) { return v; } inline UInt64 BEToSystem(UInt64 v) { return v; } inline SInt64 BEToSystem(SInt64 v) { return v; } inline float BEToSystem(float v) { return v; } inline double BEToSystem(double v) { return v; } // System (BE) to Little Endian inline UByte SystemToLE(UByte v) { return SwapOrder(v); } inline SByte SystemToLE(SByte v) { return SwapOrder(v); } inline UInt16 SystemToLE(UInt16 v) { return SwapOrder(v); } inline SInt16 SystemToLE(SInt16 v) { return SwapOrder(v); } inline UInt32 SystemToLE(UInt32 v) { return SwapOrder(v); } inline SInt32 SystemToLE(SInt32 v) { return SwapOrder(v); } inline UInt64 SystemToLE(UInt64 v) { return SwapOrder(v); } inline SInt64 SystemToLE(SInt64 v) { return SwapOrder(v); } inline float SystemToLE(float v) { return SwapOrder(v); } inline double SystemToLE(double v) { return SwapOrder(v); } // System (BE) to Big Endian inline UByte SystemToBE(UByte v) { return v; } inline SByte SystemToBE(SByte v) { return v; } inline UInt16 SystemToBE(UInt16 v) { return v; } inline SInt16 SystemToBE(SInt16 v) { return v; } inline UInt32 SystemToBE(UInt32 v) { return v; } inline SInt32 SystemToBE(SInt32 v) { return v; } inline UInt64 SystemToBE(UInt64 v) { return v; } inline SInt64 SystemToBE(SInt64 v) { return v; } inline float SystemToBE(float v) { return v; } inline double SystemToBE(double v) { return v; } #else #error "OVR_BYTE_ORDER must be defined to OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN" #endif } // namespace ByteUtil // Used primarily for hardware interfacing such as sensor reports, firmware, etc. // Reported data is all little-endian. inline UInt16 DecodeUInt16(const UByte* buffer) { return ByteUtil::LEToSystem ( *(const UInt16*)buffer ); } inline SInt16 DecodeSInt16(const UByte* buffer) { return ByteUtil::LEToSystem ( *(const SInt16*)buffer ); } inline UInt32 DecodeUInt32(const UByte* buffer) { return ByteUtil::LEToSystem ( *(const UInt32*)buffer ); } inline SInt32 DecodeSInt32(const UByte* buffer) { return ByteUtil::LEToSystem ( *(const SInt32*)buffer ); } inline float DecodeFloat(const UByte* buffer) { union { UInt32 U; float F; }; U = DecodeUInt32(buffer); return F; } inline void EncodeUInt16(UByte* buffer, UInt16 val) { *(UInt16*)buffer = ByteUtil::SystemToLE ( val ); } inline void EncodeSInt16(UByte* buffer, SInt16 val) { *(SInt16*)buffer = ByteUtil::SystemToLE ( val ); } inline void EncodeUInt32(UByte* buffer, UInt32 val) { *(UInt32*)buffer = ByteUtil::SystemToLE ( val ); } inline void EncodeSInt32(UByte* buffer, SInt32 val) { *(SInt32*)buffer = ByteUtil::SystemToLE ( val ); } inline void EncodeFloat(UByte* buffer, float val) { union { UInt32 U; float F; }; F = val; EncodeUInt32(buffer, U); } // Converts an 8-bit binary-coded decimal inline SByte DecodeBCD(UByte byte) { UByte digit1 = (byte >> 4) & 0x0f; UByte digit2 = byte & 0x0f; int decimal = digit1 * 10 + digit2; // maximum value = 99 return (SByte)decimal; } }} // OVR::Alg #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Allocator.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Allocator.cpp new file mode 100644 index 0000000..c6fc063 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Allocator.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Allocator.cpp Content : Installable memory allocator implementation Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Allocator.h" #ifdef OVR_OS_MAC #include #else #include #endif namespace OVR { //----------------------------------------------------------------------------------- // ***** Allocator Allocator* Allocator::pInstance = 0; // Default AlignedAlloc implementation will delegate to Alloc/Free after doing rounding. void* Allocator::AllocAligned(UPInt size, UPInt align) { OVR_ASSERT((align & (align-1)) == 0); align = (align > sizeof(UPInt)) ? align : sizeof(UPInt); UPInt p = (UPInt)Alloc(size+align); UPInt aligned = 0; if (p) { aligned = (UPInt(p) + align-1) & ~(align-1); if (aligned == p) aligned += align; *(((UPInt*)aligned)-1) = aligned-p; } return (void*)aligned; } void Allocator::FreeAligned(void* p) { UPInt src = UPInt(p) - *(((UPInt*)p)-1); Free((void*)src); } //------------------------------------------------------------------------ // ***** Default Allocator // This allocator is created and used if no other allocator is installed. // Default allocator delegates to system malloc. void* DefaultAllocator::Alloc(UPInt size) { return malloc(size); } void* DefaultAllocator::AllocDebug(UPInt size, const char* file, unsigned line) { #if defined(OVR_CC_MSVC) && defined(_CRTDBG_MAP_ALLOC) return _malloc_dbg(size, _NORMAL_BLOCK, file, line); #else OVR_UNUSED2(file, line); return malloc(size); #endif } void* DefaultAllocator::Realloc(void* p, UPInt newSize) { return realloc(p, newSize); } void DefaultAllocator::Free(void *p) { return free(p); } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Allocator.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Allocator.h new file mode 100644 index 0000000..17c0d7f --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Allocator.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Allocator.h Content : Installable memory allocator Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Allocator_h #define OVR_Allocator_h #include "OVR_Types.h" //----------------------------------------------------------------------------------- // ***** Disable template-unfriendly MS VC++ warnings #if defined(OVR_CC_MSVC) // Pragma to prevent long name warnings in in VC++ #pragma warning(disable : 4503) #pragma warning(disable : 4786) // In MSVC 7.1, warning about placement new POD default initializer #pragma warning(disable : 4345) #endif // Un-define new so that placement constructors work #undef new //----------------------------------------------------------------------------------- // ***** Placement new overrides // Calls constructor on own memory created with "new(ptr) type" #ifndef __PLACEMENT_NEW_INLINE #define __PLACEMENT_NEW_INLINE # if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) # include # else // Useful on MSVC OVR_FORCE_INLINE void* operator new (OVR::UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } OVR_FORCE_INLINE void operator delete (void *, void *) { } # endif #endif // __PLACEMENT_NEW_INLINE //------------------------------------------------------------------------ // ***** Macros to redefine class new/delete operators // Types specifically declared to allow disambiguation of address in // class member operator new. #define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ void* operator new(UPInt sz) \ { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ void* operator new(UPInt sz, const char* file, int line) \ { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ void operator delete(void *p) \ { check_delete(class_name, p); OVR_FREE(p); } \ void operator delete(void *p, const char*, int) \ { check_delete(class_name, p); OVR_FREE(p); } #define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ void* operator new (UPInt n, void *ptr) { OVR_UNUSED(n); return ptr; } \ void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } #define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) // Redefined all delete/new operators in a class without custom memory initialization #define OVR_MEMORY_REDEFINE_NEW(class_name) \ OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) namespace OVR { //----------------------------------------------------------------------------------- // ***** Construct / Destruct // Construct/Destruct functions are useful when new is redefined, as they can // be called instead of placement new constructors. template OVR_FORCE_INLINE T* Construct(void *p) { return ::new(p) T(); } template OVR_FORCE_INLINE T* Construct(void *p, const T& source) { return ::new(p) T(source); } // Same as above, but allows for a different type of constructor. template OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) { return ::new(p) T(source); } template OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) { return ::new(p) T(src1, src2); } template OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) { Construct(pdata); } } template OVR_FORCE_INLINE void ConstructArray(void *p, UPInt count, const T& source) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) { Construct(pdata, source); } } template OVR_FORCE_INLINE void Destruct(T *pobj) { pobj->~T(); OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. } template OVR_FORCE_INLINE void DestructArray(T *pobj, UPInt count) { for (UPInt i=0; i~T(); } //----------------------------------------------------------------------------------- // ***** Allocator // Allocator defines a memory allocation interface that developers can override // to to provide memory for OVR; an instance of this class is typically created on // application startup and passed into System or OVR::System constructor. // // // Users implementing this interface must provide three functions: Alloc, Free, // and Realloc. Implementations of these functions must honor the requested alignment. // Although arbitrary alignment requests are possible, requested alignment will // typically be small, such as 16 bytes or less. class Allocator { friend class System; public: // *** Standard Alignment Alloc/Free // Allocate memory of specified size with default alignment. // Alloc of size==0 will allocate a tiny block & return a valid pointer; // this makes it suitable for new operator. virtual void* Alloc(UPInt size) = 0; // Same as Alloc, but provides an option of passing debug data. virtual void* AllocDebug(UPInt size, const char* file, unsigned line) { OVR_UNUSED2(file, line); return Alloc(size); } // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to // new memory block, which may be the same as original pointer. Will return 0 if reallocation // failed, in which case previous memory is still valid. // Realloc to decrease size will never fail. // Realloc of pointer == 0 is equivalent to Alloc // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). virtual void* Realloc(void* p, UPInt newSize) = 0; // Frees memory allocated by Alloc/Realloc. // Free of null pointer is valid and will do nothing. virtual void Free(void *p) = 0; // *** Standard Alignment Alloc/Free // Allocate memory of specified alignment. // Memory allocated with AllocAligned MUST be freed with FreeAligned. // Default implementation will delegate to Alloc/Free after doing rounding. virtual void* AllocAligned(UPInt size, UPInt align); // Frees memory allocated with AllocAligned. virtual void FreeAligned(void* p); // Returns the pointer to the current globally installed Allocator instance. // This pointer is used for most of the memory allocations. static Allocator* GetInstance() { return pInstance; } protected: // onSystemShutdown is called on the allocator during System::Shutdown. // At this point, all allocations should've been freed. virtual void onSystemShutdown() { } public: static void setInstance(Allocator* palloc) { OVR_ASSERT((pInstance == 0) || (palloc == 0)); pInstance = palloc; } private: static Allocator* pInstance; }; //------------------------------------------------------------------------ // ***** Allocator_SingletonSupport // Allocator_SingletonSupport is a Allocator wrapper class that implements // the InitSystemSingleton static function, used to create a global singleton // used for the OVR::System default argument initialization. // // End users implementing custom Allocator interface don't need to make use of this base // class; they can just create an instance of their own class on stack and pass it to System. template class Allocator_SingletonSupport : public Allocator { struct AllocContainer { UPInt Data[(sizeof(D) + sizeof(UPInt)-1) / sizeof(UPInt)]; bool Initialized; AllocContainer() : Initialized(0) { } }; AllocContainer* pContainer; public: Allocator_SingletonSupport() : pContainer(0) { } // Creates a singleton instance of this Allocator class used // on OVR_DEFAULT_ALLOCATOR during System initialization. static D* InitSystemSingleton() { static AllocContainer Container; OVR_ASSERT(Container.Initialized == false); Allocator_SingletonSupport *presult = Construct((void*)Container.Data); presult->pContainer = &Container; Container.Initialized = true; return (D*)presult; } protected: virtual void onSystemShutdown() { Allocator::onSystemShutdown(); if (pContainer) { pContainer->Initialized = false; Destruct((D*)this); pContainer = 0; } } }; //------------------------------------------------------------------------ // ***** Default Allocator // This allocator is created and used if no other allocator is installed. // Default allocator delegates to system malloc. class DefaultAllocator : public Allocator_SingletonSupport { public: virtual void* Alloc(UPInt size); virtual void* AllocDebug(UPInt size, const char* file, unsigned line); virtual void* Realloc(void* p, UPInt newSize); virtual void Free(void *p); }; //------------------------------------------------------------------------ // ***** Memory Allocation Macros // These macros should be used for global allocation. In the future, these // macros will allows allocation to be extended with debug file/line information // if necessary. #define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) #define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) #define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) #define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) #ifdef OVR_BUILD_DEBUG #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) #else #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) #endif //------------------------------------------------------------------------ // Base class that overrides the new and delete operators. // Deriving from this class, even as a multiple base, incurs no space overhead. class NewOverrideBase { public: // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) }; } // OVR // Redefine operator 'new' if necessary. #if defined(OVR_DEFINE_NEW) #define new OVR_DEFINE_NEW #endif #endif // OVR_Memory \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Array.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Array.h new file mode 100644 index 0000000..44db492 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Array.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Array.h Content : Template implementation for Array Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Array_h #define OVR_Array_h #include "OVR_ContainerAllocator.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** ArrayDefaultPolicy // // Default resize behavior. No minimal capacity, Granularity=4, // Shrinking as needed. ArrayConstPolicy actually is the same as // ArrayDefaultPolicy, but parametrized with constants. // This struct is used only in order to reduce the template "matroska". struct ArrayDefaultPolicy { ArrayDefaultPolicy() : Capacity(0) {} ArrayDefaultPolicy(const ArrayDefaultPolicy&) : Capacity(0) {} UPInt GetMinCapacity() const { return 0; } UPInt GetGranularity() const { return 4; } bool NeverShrinking() const { return 0; } UPInt GetCapacity() const { return Capacity; } void SetCapacity(UPInt capacity) { Capacity = capacity; } private: UPInt Capacity; }; //----------------------------------------------------------------------------------- // ***** ArrayConstPolicy // // Statically parametrized resizing behavior: // MinCapacity, Granularity, and Shrinking flag. template struct ArrayConstPolicy { typedef ArrayConstPolicy SelfType; ArrayConstPolicy() : Capacity(0) {} ArrayConstPolicy(const SelfType&) : Capacity(0) {} UPInt GetMinCapacity() const { return MinCapacity; } UPInt GetGranularity() const { return Granularity; } bool NeverShrinking() const { return NeverShrink; } UPInt GetCapacity() const { return Capacity; } void SetCapacity(UPInt capacity) { Capacity = capacity; } private: UPInt Capacity; }; //----------------------------------------------------------------------------------- // ***** ArrayDataBase // // Basic operations with array data: Reserve, Resize, Free, ArrayPolicy. // For internal use only: ArrayData,ArrayDataCC and others. template struct ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase SelfType; ArrayDataBase() : Data(0), Size(0), Policy() {} ArrayDataBase(const SizePolicy& p) : Data(0), Size(0), Policy(p) {} ~ArrayDataBase() { Allocator::DestructArray(Data, Size); Allocator::Free(Data); } UPInt GetCapacity() const { return Policy.GetCapacity(); } void ClearAndRelease() { Allocator::DestructArray(Data, Size); Allocator::Free(Data); Data = 0; Size = 0; Policy.SetCapacity(0); } void Reserve(UPInt newCapacity) { if (Policy.NeverShrinking() && newCapacity < GetCapacity()) return; if (newCapacity < Policy.GetMinCapacity()) newCapacity = Policy.GetMinCapacity(); // Resize the buffer. if (newCapacity == 0) { if (Data) { Allocator::Free(Data); Data = 0; } Policy.SetCapacity(0); } else { UPInt gran = Policy.GetGranularity(); newCapacity = (newCapacity + gran - 1) / gran * gran; if (Data) { if (Allocator::IsMovable()) { Data = (T*)Allocator::Realloc(Data, sizeof(T) * newCapacity); } else { T* newData = (T*)Allocator::Alloc(sizeof(T) * newCapacity); UPInt i, s; s = (Size < newCapacity) ? Size : newCapacity; for (i = 0; i < s; ++i) { Allocator::Construct(&newData[i], Data[i]); Allocator::Destruct(&Data[i]); } for (i = s; i < Size; ++i) { Allocator::Destruct(&Data[i]); } Allocator::Free(Data); Data = newData; } } else { Data = (T*)Allocator::Alloc(sizeof(T) * newCapacity); //memset(Buffer, 0, (sizeof(ValueType) * newSize)); // Do we need this? } Policy.SetCapacity(newCapacity); // OVR_ASSERT(Data); // need to throw (or something) on alloc failure! } } // This version of Resize DOES NOT construct the elements. // It's done to optimize PushBack, which uses a copy constructor // instead of the default constructor and assignment void ResizeNoConstruct(UPInt newSize) { UPInt oldSize = Size; if (newSize < oldSize) { Allocator::DestructArray(Data + newSize, oldSize - newSize); if (newSize < (Policy.GetCapacity() >> 1)) { Reserve(newSize); } } else if(newSize >= Policy.GetCapacity()) { Reserve(newSize + (newSize >> 2)); } //! IMPORTANT to modify Size only after Reserve completes, because garbage collectable // array may use this array and may traverse it during Reserve (in the case, if // collection occurs because of heap limit exceeded). Size = newSize; } ValueType* Data; UPInt Size; SizePolicy Policy; }; //----------------------------------------------------------------------------------- // ***** ArrayData // // General purpose array data. // For internal use only in Array, ArrayLH, ArrayPOD and so on. template struct ArrayData : ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase BaseType; typedef ArrayData SelfType; ArrayData() : BaseType() { } ArrayData(UPInt size) : BaseType() { Resize(size); } ArrayData(const SelfType& a) : BaseType(a.Policy) { Append(a.Data, a.Size); } void Resize(UPInt newSize) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(newSize); if(newSize > oldSize) Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize); } void PushBack(const ValueType& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::Construct(this->Data + this->Size - 1, val); } template void PushBackAlt(const S& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::ConstructAlt(this->Data + this->Size - 1, val); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { if (count) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(this->Size + count); Allocator::ConstructArray(this->Data + oldSize, count, other); } } }; //----------------------------------------------------------------------------------- // ***** ArrayDataCC // // A modification of ArrayData that always copy-constructs new elements // using a specified DefaultValue. For internal use only in ArrayCC. template struct ArrayDataCC : ArrayDataBase { typedef T ValueType; typedef Allocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayDataBase BaseType; typedef ArrayDataCC SelfType; ArrayDataCC(const ValueType& defval) : BaseType(), DefaultValue(defval) { } ArrayDataCC(const ValueType& defval, UPInt size) : BaseType(), DefaultValue(defval) { Resize(size); } ArrayDataCC(const SelfType& a) : BaseType(a.Policy), DefaultValue(a.DefaultValue) { Append(a.Data, a.Size); } void Resize(UPInt newSize) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(newSize); if(newSize > oldSize) Allocator::ConstructArray(this->Data + oldSize, newSize - oldSize, DefaultValue); } void PushBack(const ValueType& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::Construct(this->Data + this->Size - 1, val); } template void PushBackAlt(const S& val) { BaseType::ResizeNoConstruct(this->Size + 1); Allocator::ConstructAlt(this->Data + this->Size - 1, val); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { if (count) { UPInt oldSize = this->Size; BaseType::ResizeNoConstruct(this->Size + count); Allocator::ConstructArray(this->Data + oldSize, count, other); } } ValueType DefaultValue; }; //----------------------------------------------------------------------------------- // ***** ArrayBase // // Resizable array. The behavior can be POD (suffix _POD) and // Movable (no suffix) depending on the allocator policy. // In case of _POD the constructors and destructors are not called. // // Arrays can't handle non-movable objects! Don't put anything in here // that can't be moved around by bitwise copy. // // The addresses of elements are not persistent! Don't keep the address // of an element; the array contents will move around as it gets resized. template class ArrayBase { public: typedef typename ArrayData::ValueType ValueType; typedef typename ArrayData::AllocatorType AllocatorType; typedef typename ArrayData::SizePolicyType SizePolicyType; typedef ArrayBase SelfType; #undef new OVR_MEMORY_REDEFINE_NEW(ArrayBase) // Redefine operator 'new' if necessary. #if defined(OVR_DEFINE_NEW) #define new OVR_DEFINE_NEW #endif ArrayBase() : Data() {} ArrayBase(UPInt size) : Data(size) {} ArrayBase(const SelfType& a) : Data(a.Data) {} ArrayBase(const ValueType& defval) : Data(defval) {} ArrayBase(const ValueType& defval, UPInt size) : Data(defval, size) {} SizePolicyType* GetSizePolicy() const { return Data.Policy; } void SetSizePolicy(const SizePolicyType& p) { Data.Policy = p; } bool NeverShrinking()const { return Data.Policy.NeverShrinking(); } UPInt GetSize() const { return Data.Size; } bool IsEmpty() const { return Data.Size == 0; } UPInt GetCapacity() const { return Data.GetCapacity(); } UPInt GetNumBytes() const { return Data.GetCapacity() * sizeof(ValueType); } void ClearAndRelease() { Data.ClearAndRelease(); } void Clear() { Data.Resize(0); } void Resize(UPInt newSize) { Data.Resize(newSize); } // Reserve can only increase the capacity void Reserve(UPInt newCapacity) { if (newCapacity > Data.GetCapacity()) Data.Reserve(newCapacity); } // Basic access. ValueType& At(UPInt index) { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } const ValueType& At(UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } ValueType ValueAt(UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } // Basic access. ValueType& operator [] (UPInt index) { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } const ValueType& operator [] (UPInt index) const { OVR_ASSERT(index < Data.Size); return Data.Data[index]; } // Raw pointer to the data. Use with caution! const ValueType* GetDataPtr() const { return Data.Data; } ValueType* GetDataPtr() { return Data.Data; } // Insert the given element at the end of the array. void PushBack(const ValueType& val) { // DO NOT pass elements of your own vector into // push_back()! Since we're using references, // resize() may munge the element storage! // OVR_ASSERT(&val < &Buffer[0] || &val > &Buffer[BufferSize]); Data.PushBack(val); } template void PushBackAlt(const S& val) { Data.PushBackAlt(val); } // Remove the last element. void PopBack(UPInt count = 1) { OVR_ASSERT(Data.Size >= count); Data.Resize(Data.Size - count); } ValueType& PushDefault() { Data.PushBack(ValueType()); return Back(); } ValueType Pop() { ValueType t = Back(); PopBack(); return t; } // Access the first element. ValueType& Front() { return At(0); } const ValueType& Front() const { return At(0); } // Access the last element. ValueType& Back() { return At(Data.Size - 1); } const ValueType& Back() const { return At(Data.Size - 1); } // Array copy. Copies the contents of a into this array. const SelfType& operator = (const SelfType& a) { Resize(a.GetSize()); for (UPInt i = 0; i < Data.Size; i++) { *(Data.Data + i) = a[i]; } return *this; } // Removing multiple elements from the array. void RemoveMultipleAt(UPInt index, UPInt num) { OVR_ASSERT(index + num <= Data.Size); if (Data.Size == num) { Clear(); } else { AllocatorType::DestructArray(Data.Data + index, num); AllocatorType::CopyArrayForward( Data.Data + index, Data.Data + index + num, Data.Size - num - index); Data.Size -= num; } } // Removing an element from the array is an expensive operation! // It compacts only after removing the last element. // If order of elements in the array is not important then use // RemoveAtUnordered, that could be much faster than the regular // RemoveAt. void RemoveAt(UPInt index) { OVR_ASSERT(index < Data.Size); if (Data.Size == 1) { Clear(); } else { AllocatorType::Destruct(Data.Data + index); AllocatorType::CopyArrayForward( Data.Data + index, Data.Data + index + 1, Data.Size - 1 - index); --Data.Size; } } // Removes an element from the array without respecting of original order of // elements for better performance. Do not use on array where order of elements // is important, otherwise use it instead of regular RemoveAt(). void RemoveAtUnordered(UPInt index) { OVR_ASSERT(index < Data.Size); if (Data.Size == 1) { Clear(); } else { // copy the last element into the 'index' position // and decrement the size (instead of moving all elements // in [index + 1 .. size - 1] range). const UPInt lastElemIndex = Data.Size - 1; if (index < lastElemIndex) { AllocatorType::Destruct(Data.Data + index); AllocatorType::Construct(Data.Data + index, Data.Data[lastElemIndex]); } AllocatorType::Destruct(Data.Data + lastElemIndex); --Data.Size; } } // Insert the given object at the given index shifting all the elements up. void InsertAt(UPInt index, const ValueType& val = ValueType()) { OVR_ASSERT(index <= Data.Size); Data.Resize(Data.Size + 1); if (index < Data.Size - 1) { AllocatorType::CopyArrayBackward( Data.Data + index + 1, Data.Data + index, Data.Size - 1 - index); } AllocatorType::Construct(Data.Data + index, val); } // Insert the given object at the given index shifting all the elements up. void InsertMultipleAt(UPInt index, UPInt num, const ValueType& val = ValueType()) { OVR_ASSERT(index <= Data.Size); Data.Resize(Data.Size + num); if (index < Data.Size - num) { AllocatorType::CopyArrayBackward( Data.Data + index + num, Data.Data + index, Data.Size - num - index); } for (UPInt i = 0; i < num; ++i) AllocatorType::Construct(Data.Data + index + i, val); } // Append the given data to the array. void Append(const SelfType& other) { Append(other.Data.Data, other.GetSize()); } // Append the given data to the array. void Append(const ValueType other[], UPInt count) { Data.Append(other, count); } class Iterator { SelfType* pArray; SPInt CurIndex; public: Iterator() : pArray(0), CurIndex(-1) {} Iterator(SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} bool operator==(const Iterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } bool operator!=(const Iterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } Iterator& operator++() { if (pArray) { if (CurIndex < (SPInt)pArray->GetSize()) ++CurIndex; } return *this; } Iterator operator++(int) { Iterator it(*this); operator++(); return it; } Iterator& operator--() { if (pArray) { if (CurIndex >= 0) --CurIndex; } return *this; } Iterator operator--(int) { Iterator it(*this); operator--(); return it; } Iterator operator+(int delta) const { return Iterator(pArray, CurIndex + delta); } Iterator operator-(int delta) const { return Iterator(pArray, CurIndex - delta); } SPInt operator-(const Iterator& right) const { OVR_ASSERT(pArray == right.pArray); return CurIndex - right.CurIndex; } ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } void Remove() { if (!IsFinished()) pArray->RemoveAt(CurIndex); } SPInt GetIndex() const { return CurIndex; } }; Iterator Begin() { return Iterator(this); } Iterator End() { return Iterator(this, (SPInt)GetSize()); } Iterator Last() { return Iterator(this, (SPInt)GetSize() - 1); } class ConstIterator { const SelfType* pArray; SPInt CurIndex; public: ConstIterator() : pArray(0), CurIndex(-1) {} ConstIterator(const SelfType* parr, SPInt idx = 0) : pArray(parr), CurIndex(idx) {} bool operator==(const ConstIterator& it) const { return pArray == it.pArray && CurIndex == it.CurIndex; } bool operator!=(const ConstIterator& it) const { return pArray != it.pArray || CurIndex != it.CurIndex; } ConstIterator& operator++() { if (pArray) { if (CurIndex < (int)pArray->GetSize()) ++CurIndex; } return *this; } ConstIterator operator++(int) { ConstIterator it(*this); operator++(); return it; } ConstIterator& operator--() { if (pArray) { if (CurIndex >= 0) --CurIndex; } return *this; } ConstIterator operator--(int) { ConstIterator it(*this); operator--(); return it; } ConstIterator operator+(int delta) const { return ConstIterator(pArray, CurIndex + delta); } ConstIterator operator-(int delta) const { return ConstIterator(pArray, CurIndex - delta); } SPInt operator-(const ConstIterator& right) const { OVR_ASSERT(pArray == right.pArray); return CurIndex - right.CurIndex; } const ValueType& operator*() const { OVR_ASSERT(pArray); return (*pArray)[CurIndex]; } const ValueType* operator->() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } const ValueType* GetPtr() const { OVR_ASSERT(pArray); return &(*pArray)[CurIndex]; } bool IsFinished() const { return !pArray || CurIndex < 0 || CurIndex >= (int)pArray->GetSize(); } SPInt GetIndex() const { return CurIndex; } }; ConstIterator Begin() const { return ConstIterator(this); } ConstIterator End() const { return ConstIterator(this, (SPInt)GetSize()); } ConstIterator Last() const { return ConstIterator(this, (SPInt)GetSize() - 1); } protected: ArrayData Data; }; //----------------------------------------------------------------------------------- // ***** Array // // General purpose array for movable objects that require explicit // construction/destruction. template class Array : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator AllocatorType; typedef SizePolicy SizePolicyType; typedef Array SelfType; typedef ArrayBase, SizePolicy> > BaseType; Array() : BaseType() {} Array(UPInt size) : BaseType(size) {} Array(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } Array(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayPOD // // General purpose array for movable objects that DOES NOT require // construction/destruction. Constructors and destructors are not called! // Global heap is in use. template class ArrayPOD : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator_POD AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayPOD SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayPOD() : BaseType() {} ArrayPOD(UPInt size) : BaseType(size) {} ArrayPOD(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } ArrayPOD(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayCPP // // General purpose, fully C++ compliant array. Can be used with non-movable data. // Global heap is in use. template class ArrayCPP : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator_CPP AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayCPP SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayCPP() : BaseType() {} ArrayCPP(UPInt size) : BaseType(size) {} ArrayCPP(const SizePolicyType& p) : BaseType() { SetSizePolicy(p); } ArrayCPP(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; // ***** ArrayCC // // A modification of the array that uses the given default value to // construct the elements. The constructors and destructors are // properly called, the objects must be movable. template class ArrayCC : public ArrayBase, SizePolicy> > { public: typedef T ValueType; typedef ContainerAllocator AllocatorType; typedef SizePolicy SizePolicyType; typedef ArrayCC SelfType; typedef ArrayBase, SizePolicy> > BaseType; ArrayCC(const ValueType& defval) : BaseType(defval) {} ArrayCC(const ValueType& defval, UPInt size) : BaseType(defval, size) {} ArrayCC(const ValueType& defval, const SizePolicyType& p) : BaseType(defval) { SetSizePolicy(p); } ArrayCC(const SelfType& a) : BaseType(a) {} const SelfType& operator=(const SelfType& a) { BaseType::operator=(a); return *this; } }; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Atomic.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Atomic.cpp new file mode 100644 index 0000000..58f030e --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Atomic.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Atomic.cpp Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementation if not available. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Atomic.h" #include "OVR_Allocator.h" #ifdef OVR_ENABLE_THREADS // Include Windows 8-Metro compatible Synchronization API #if defined(OVR_OS_WIN32) && defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) #include #endif namespace OVR { // ***** Windows Lock implementation #if defined(OVR_OS_WIN32) // ***** Standard Win32 Lock implementation // Constructors Lock::Lock(unsigned spinCount) { #if defined(NTDDI_WIN8) && (NTDDI_VERSION >= NTDDI_WIN8) // On Windows 8 we use InitializeCriticalSectionEx due to Metro-Compatibility InitializeCriticalSectionEx(&cs, spinCount, OVR_DEBUG_SELECT(NULL, CRITICAL_SECTION_NO_DEBUG_INFO)); #else // Spin count init critical section function prototype for Window NT typedef BOOL (WINAPI *Function_InitializeCriticalSectionAndSpinCount) (LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); // Try to load function dynamically so that we don't require NT // On Windows NT we will use InitializeCriticalSectionAndSpinCount static bool initTried = 0; static Function_InitializeCriticalSectionAndSpinCount pInitFn = 0; if (!initTried) { HMODULE hmodule = ::LoadLibrary(OVR_STR("kernel32.dll")); pInitFn = (Function_InitializeCriticalSectionAndSpinCount) ::GetProcAddress(hmodule, "InitializeCriticalSectionAndSpinCount"); initTried = true; } // Initialize the critical section if (pInitFn) pInitFn(&cs, spinCount); else ::InitializeCriticalSection(&cs); #endif } Lock::~Lock() { DeleteCriticalSection(&cs); } #endif //------------------------------------------------------------------------------------- // ***** SharedLock // This is a general purpose globally shared Lock implementation that should probably be // moved to Kernel. // May in theory busy spin-wait if we hit contention on first lock creation, // but this shouldn't matter in practice since Lock* should be cached. enum { LockInitMarker = 0xFFFFFFFF }; Lock* SharedLock::GetLockAddRef() { int oldUseCount; do { oldUseCount = UseCount; if (oldUseCount == LockInitMarker) continue; if (oldUseCount == 0) { // Initialize marker if (AtomicOps::CompareAndSet_Sync(&UseCount, 0, LockInitMarker)) { Construct(Buffer); do { } while (!AtomicOps::CompareAndSet_Sync(&UseCount, LockInitMarker, 1)); return toLock(); } continue; } } while (!AtomicOps::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount + 1)); return toLock(); } void SharedLock::ReleaseLock(Lock* plock) { OVR_UNUSED(plock); OVR_ASSERT(plock == toLock()); int oldUseCount; do { oldUseCount = UseCount; OVR_ASSERT(oldUseCount != LockInitMarker); if (oldUseCount == 1) { // Initialize marker if (AtomicOps::CompareAndSet_Sync(&UseCount, 1, LockInitMarker)) { Destruct(toLock()); do { } while (!AtomicOps::CompareAndSet_Sync(&UseCount, LockInitMarker, 0)); return; } continue; } } while (!AtomicOps::CompareAndSet_NoSync(&UseCount, oldUseCount, oldUseCount - 1)); } } // OVR #endif // OVR_ENABLE_THREADS \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Atomic.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Atomic.h new file mode 100644 index 0000000..a6521a9 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Atomic.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Atomic.h Content : Contains atomic operations and inline fastest locking functionality. Will contain #ifdefs for OS efficiency. Have non-thread-safe implementaion if not available. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Atomic_h #define OVR_Atomic_h #include "OVR_Types.h" // Include System thread functionality. #if defined(OVR_OS_WIN32) #include #else #include #endif namespace OVR { // ****** Declared classes // If there is NO thread support we implement AtomicOps and // Lock objects as no-ops. The other classes are not defined. template class AtomicOps; template class AtomicInt; template class AtomicPtr; class Lock; //----------------------------------------------------------------------------------- // ***** AtomicOps // Atomic operations are provided by the AtomicOps templates class, // implemented through system-specific AtomicOpsRaw specializations. // It provides several fundamental operations such as Exchange, ExchangeAdd // CompareAndSet, and Store_Release. Each function includes several memory // synchronization versions, important for multiprocessing CPUs with weak // memory consistency. The following memory fencing strategies are supported: // // - NoSync. No memory synchronization is done for atomic op. // - Release. All other memory writes are completed before atomic op // writes its results. // - Acquire. Further memory reads are forced to wait until atomic op // executes, guaranteeing that the right values will be seen. // - Sync. A combination of Release and Acquire. // *** AtomicOpsRaw // AtomicOpsRaw is a specialized template that provides atomic operations // used by AtomicOps. This class has two fundamental qualities: (1) it // defines a type T of correct size, and (2) provides operations that work // atomically, such as Exchange_Sync and CompareAndSet_Release. // AtomicOpsRawBase class contains shared constants/classes for AtomicOpsRaw. // The primary thing is does is define sync class objects, whose destructor and // constructor provide places to insert appropriate synchronization calls, on // systems where such calls are necessary. So far, the breakdown is as follows: // // - X86 systems don't need custom syncs, since their exchange/atomic // instructions are implicitly synchronized. // - PowerPC requires lwsync/isync instructions that can use this mechanism. // - If some other systems require a mechanism where syncing type is associated // with a particular instruction, the default implementation (which implements // all Sync, Acquire, and Release modes in terms of NoSync and fence) may not // work. Ii that case it will need to be #ifdef-ed conditionally. struct AtomicOpsRawBase { #if !defined(OVR_ENABLE_THREADS) || defined(OVR_CPU_X86) || defined(OVR_OS_WIN32) || defined(OVR_OS_IPHONE) // Need to have empty constructor to avoid class 'unused' variable warning. struct FullSync { inline FullSync() { } }; struct AcquireSync { inline AcquireSync() { } }; struct ReleaseSync { inline ReleaseSync() { } }; #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("isync\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("isync\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; #elif defined(OVR_CPU_MIPS) struct FullSync { inline FullSync() { asm volatile("sync\n"); } ~FullSync() { asm volatile("sync\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("sync\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("sync\n"); } }; #elif defined(OVR_CPU_ARM) struct FullSync { inline FullSync() { asm volatile("dmb\n"); } ~FullSync() { asm volatile("dmb\n"); } }; struct AcquireSync { inline AcquireSync() { } ~AcquireSync() { asm volatile("dmb\n"); } }; struct ReleaseSync { inline ReleaseSync() { asm volatile("dmb\n"); } }; #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4) // __sync functions are already full sync struct FullSync { inline FullSync() { } }; struct AcquireSync { inline AcquireSync() { } }; struct ReleaseSync { inline ReleaseSync() { } }; #endif }; // 4-Byte raw data atomic op implementation class. struct AtomicOpsRaw_4ByteImpl : public AtomicOpsRawBase { #if !defined(OVR_ENABLE_THREADS) // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. typedef UInt32 T; // *** Thread - Safe Atomic Versions. #elif defined(OVR_OS_WIN32) // Use special defined for VC6, where volatile is not used and // InterlockedCompareExchange is declared incorrectly. typedef LONG T; #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC < 1300) typedef T* InterlockTPtr; typedef LPVOID ET; typedef ET* InterlockETPtr; #else typedef volatile T* InterlockTPtr; typedef T ET; typedef InterlockTPtr InterlockETPtr; #endif inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange((InterlockTPtr)p, val); } inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd((InterlockTPtr)p, val); } inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange((InterlockETPtr)p, (ET)val, (ET)c) == (ET)c; } #elif defined(OVR_CPU_PPC64) || defined(OVR_CPU_PPC) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "stwcx. %[j],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 dummy, ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "add %[o],%[r],%[j]\n\t" "stwcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret; asm volatile("1:\n\t" "lwarx %[r],0,%[i]\n\t" "cmpw 0,%[r],%[cmp]\n\t" "mfcr %[r]\n\t" "bne- 2f\n\t" "stwcx. %[val],0,%[i]\n\t" "bne- 1b\n\t" "2:\n" : "+m" (*i), [r] "=&b" (ret) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc", "memory"); return (ret & 0x20000000) ? 1 : 0; } #elif defined(OVR_CPU_MIPS) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "ll %[r],0(%[i])\n\t" "sc %[j],0(%[i])\n\t" "beq %[j],$0,1b\n\t" "nop \n" : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret; asm volatile("1:\n\t" "ll %[r],0(%[i])\n\t" "addu %[j],%[r],%[j]\n\t" "sc %[j],0(%[i])\n\t" "beq %[j],$0,1b\n\t" "nop \n" : "+m" (*i), [r] "=&d" (ret) : [i] "d" (i), [j] "d" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret, dummy; asm volatile("1:\n\t" "move %[r],$0\n\t" "ll %[o],0(%[i])\n\t" "bne %[o],%[c],2f\n\t" "move %[r],%[v]\n\t" "sc %[r],0(%[i])\n\t" "beq %[r],$0,1b\n\t" "nop \n\t" "2:\n" : "+m" (*i),[r] "=&d" (ret), [o] "=&d" (dummy) : [i] "d" (i), [c] "d" (c), [v] "d" (value) : "cc", "memory"); return ret; } #elif defined(OVR_CPU_ARM) && defined(OVR_CC_ARM) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { for(;;) { T r = __ldrex(i); if (__strex(j, i) == 0) return r; } } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { for(;;) { T r = __ldrex(i); if (__strex(r + j, i) == 0) return r; } } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { for(;;) { T r = __ldrex(i); if (r != c) return 0; if (__strex(value, i) == 0) return 1; } } #elif defined(OVR_CPU_ARM) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret, dummy; asm volatile("1:\n\t" "ldrex %[r],[%[i]]\n\t" "strex %[t],%[j],[%[i]]\n\t" "cmp %[t],#0\n\t" "bne 1b\n\t" : "+m" (*i), [r] "=&r" (ret), [t] "=&r" (dummy) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); return ret; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { UInt32 ret, dummy, test; asm volatile("1:\n\t" "ldrex %[r],[%[i]]\n\t" "add %[o],%[r],%[j]\n\t" "strex %[t],%[o],[%[i]]\n\t" "cmp %[t],#0\n\t" "bne 1b\n\t" : "+m" (*i), [r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [j] "r" (j) : "cc", "memory"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret = 1, dummy, test; asm volatile("1:\n\t" "ldrex %[o],[%[i]]\n\t" "cmp %[o],%[c]\n\t" "bne 2f\n\t" "strex %[r],%[v],[%[i]]\n\t" "cmp %[r],#0\n\t" "bne 1b\n\t" "2:\n" : "+m" (*i),[r] "=&r" (ret), [o] "=&r" (dummy), [t] "=&r" (test) : [i] "r" (i), [c] "r" (c), [v] "r" (value) : "cc", "memory"); return !ret; } #elif defined(OVR_CPU_X86) typedef UInt32 T; static inline UInt32 Exchange_NoSync(volatile UInt32 *i, UInt32 j) { asm volatile("xchgl %1,%[i]\n" : "+m" (*i), "=q" (j) : [i] "m" (*i), "1" (j) : "cc", "memory"); return j; } static inline UInt32 ExchangeAdd_NoSync(volatile UInt32 *i, UInt32 j) { asm volatile("lock; xaddl %1,%[i]\n" : "+m" (*i), "+q" (j) : [i] "m" (*i) : "cc", "memory"); return j; } static inline bool CompareAndSet_NoSync(volatile UInt32 *i, UInt32 c, UInt32 value) { UInt32 ret; asm volatile("lock; cmpxchgl %[v],%[i]\n" : "+m" (*i), "=a" (ret) : [i] "m" (*i), "1" (c), [v] "q" (value) : "cc", "memory"); return (ret == c); } #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) typedef UInt32 T; static inline T Exchange_NoSync(volatile T *i, T j) { T v; do { v = *i; } while (!__sync_bool_compare_and_swap(i, v, j)); return v; } static inline T ExchangeAdd_NoSync(volatile T *i, T j) { return __sync_fetch_and_add(i, j); } static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) { return __sync_bool_compare_and_swap(i, c, value); } #endif // OS }; // 8-Byte raw data data atomic op implementation class. // Currently implementation is provided only on systems with 64-bit pointers. struct AtomicOpsRaw_8ByteImpl : public AtomicOpsRawBase { #if !defined(OVR_64BIT_POINTERS) || !defined(OVR_ENABLE_THREADS) // Provide a type for no-thread-support cases. Used by AtomicOpsRaw_DefImpl. typedef UInt64 T; // *** Thread - Safe OS specific versions. #elif defined(OVR_OS_WIN32) // This is only for 64-bit systems. typedef LONG64 T; typedef volatile T* InterlockTPtr; inline static T Exchange_NoSync(volatile T* p, T val) { return InterlockedExchange64((InterlockTPtr)p, val); } inline static T ExchangeAdd_NoSync(volatile T* p, T val) { return InterlockedExchangeAdd64((InterlockTPtr)p, val); } inline static bool CompareAndSet_NoSync(volatile T* p, T c, T val) { return InterlockedCompareExchange64((InterlockTPtr)p, val, c) == c; } #elif defined(OVR_CPU_PPC64) typedef UInt64 T; static inline UInt64 Exchange_NoSync(volatile UInt64 *i, UInt64 j) { UInt64 dummy, ret; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "mr %[o],%[j]\n\t" "stdcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); return ret; } static inline UInt64 ExchangeAdd_NoSync(volatile UInt64 *i, UInt64 j) { UInt64 dummy, ret; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "add %[o],%[r],%[j]\n\t" "stdcx. %[o],0,%[i]\n\t" "bne- 1b\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [j] "b" (j) : "cc"); return ret; } static inline bool CompareAndSet_NoSync(volatile UInt64 *i, UInt64 c, UInt64 value) { UInt64 ret, dummy; asm volatile("1:\n\t" "ldarx %[r],0,%[i]\n\t" "cmpw 0,%[r],%[cmp]\n\t" "mfcr %[r]\n\t" "bne- 2f\n\t" "stdcx. %[val],0,%[i]\n\t" "bne- 1b\n\t" "2:\n" : "+m" (*i), [r] "=&b" (ret), [o] "=&r" (dummy) : [i] "b" (i), [cmp] "b" (c), [val] "b" (value) : "cc"); return (ret & 0x20000000) ? 1 : 0; } #elif defined(OVR_CC_GNU) && (__GNUC__ >= 4 && __GNUC_MINOR__ >= 1) typedef UInt64 T; static inline T Exchange_NoSync(volatile T *i, T j) { T v; do { v = *i; } while (!__sync_bool_compare_and_swap(i, v, j)); return v; } static inline T ExchangeAdd_NoSync(volatile T *i, T j) { return __sync_fetch_and_add(i, j); } static inline bool CompareAndSet_NoSync(volatile T *i, T c, T value) { return __sync_bool_compare_and_swap(i, c, value); } #endif // OS }; // Default implementation for AtomicOpsRaw; provides implementation of mem-fenced // atomic operations where fencing is done with a sync object wrapped around a NoSync // operation implemented in the base class. If such implementation is not possible // on a given platform, #ifdefs can be used to disable it and then op functions can be // implemented individually in the appropriate AtomicOpsRaw class. template struct AtomicOpsRaw_DefImpl : public O { typedef typename O::T O_T; typedef typename O::FullSync O_FullSync; typedef typename O::AcquireSync O_AcquireSync; typedef typename O::ReleaseSync O_ReleaseSync; // If there is no thread support, provide the default implementation. In this case, // the base class (0) must still provide the T declaration. #ifndef OVR_ENABLE_THREADS // Atomic exchange of val with argument. Returns old val. inline static O_T Exchange_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p = val; return old; } // Adds a new val to argument; returns its old val. inline static O_T ExchangeAdd_NoSync(volatile O_T* p, O_T val) { O_T old = *p; *p += val; return old; } // Compares the argument data with 'c' val. // If succeeded, stores val int '*p' and returns true; otherwise returns false. inline static bool CompareAndSet_NoSync(volatile O_T* p, O_T c, O_T val) { if (*p==c) { *p = val; return 1; } return 0; } #endif // If NoSync wrapped implementation may not be possible, it this block should be // replaced with per-function implementation in O. // "AtomicOpsRaw_DefImpl::" prefix in calls below. inline static O_T Exchange_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T Exchange_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T Exchange_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::Exchange_NoSync(p, val); } inline static O_T ExchangeAdd_Sync(volatile O_T* p, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static O_T ExchangeAdd_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static O_T ExchangeAdd_Acquire(volatile O_T* p, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::ExchangeAdd_NoSync(p, val); } inline static bool CompareAndSet_Sync(volatile O_T* p, O_T c, O_T val) { O_FullSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } inline static bool CompareAndSet_Release(volatile O_T* p, O_T c, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } inline static bool CompareAndSet_Acquire(volatile O_T* p, O_T c, O_T val) { O_AcquireSync sync; OVR_UNUSED(sync); return AtomicOpsRaw_DefImpl::CompareAndSet_NoSync(p,c,val); } // Loads and stores with memory fence. These have only the relevant versions. #ifdef OVR_CPU_X86 // On X86, Store_Release is implemented as exchange. Note that we can also // consider 'sfence' in the future, although it is not as compatible with older CPUs. inline static void Store_Release(volatile O_T* p, O_T val) { Exchange_Release(p, val); } #else inline static void Store_Release(volatile O_T* p, O_T val) { O_ReleaseSync sync; OVR_UNUSED(sync); *p = val; } #endif inline static O_T Load_Acquire(const volatile O_T* p) { O_AcquireSync sync; OVR_UNUSED(sync); return *p; } }; template struct AtomicOpsRaw : public AtomicOpsRawBase { }; template<> struct AtomicOpsRaw<4> : public AtomicOpsRaw_DefImpl { // Ensure that assigned type size is correct. AtomicOpsRaw() { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 4); } }; template<> struct AtomicOpsRaw<8> : public AtomicOpsRaw_DefImpl { AtomicOpsRaw() { OVR_COMPILER_ASSERT(sizeof(AtomicOpsRaw_DefImpl::T) == 8); } }; // *** AtomicOps - implementation of atomic Ops for specified class // Implements atomic ops on a class, provided that the object is either // 4 or 8 bytes in size (depending on the AtomicOpsRaw specializations // available). Relies on AtomicOpsRaw for much of implementation. template class AtomicOps { typedef AtomicOpsRaw Ops; typedef typename Ops::T T; typedef volatile typename Ops::T* PT; // We cast through unions to (1) avoid pointer size compiler warnings // and (2) ensure that there are no problems with strict pointer aliasing. union C2T_union { C c; T t; }; public: // General purpose implementation for standard syncs. inline static C Exchange_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Sync((PT)p, u.t); return u.c; } inline static C Exchange_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Release((PT)p, u.t); return u.c; } inline static C Exchange_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_Acquire((PT)p, u.t); return u.c; } inline static C Exchange_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::Exchange_NoSync((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Sync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Sync((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Release(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Release((PT)p, u.t); return u.c; } inline static C ExchangeAdd_Acquire(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_Acquire((PT)p, u.t); return u.c; } inline static C ExchangeAdd_NoSync(volatile C* p, C val) { C2T_union u; u.c = val; u.t = Ops::ExchangeAdd_NoSync((PT)p, u.t); return u.c; } inline static bool CompareAndSet_Sync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Sync((PT)p, cu.t, u.t); } inline static bool CompareAndSet_Release(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Release((PT)p, cu.t, u.t); } inline static bool CompareAndSet_Relse(volatile C* p, C c, C val){ C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_Acquire((PT)p, cu.t, u.t); } inline static bool CompareAndSet_NoSync(volatile C* p, C c, C val) { C2T_union u,cu; u.c = val; cu.c = c; return Ops::CompareAndSet_NoSync((PT)p, cu.t, u.t); } // Loads and stores with memory fence. These have only the relevant versions. inline static void Store_Release(volatile C* p, C val) { C2T_union u; u.c = val; Ops::Store_Release((PT)p, u.t); } inline static C Load_Acquire(const volatile C* p) { C2T_union u; u.t = Ops::Load_Acquire((PT)p); return u.c; } }; // Atomic value base class - implements operations shared for integers and pointers. template class AtomicValueBase { protected: typedef AtomicOps Ops; public: volatile T Value; inline AtomicValueBase() { } explicit inline AtomicValueBase(T val) { Ops::Store_Release(&Value, val); } // Most libraries (TBB and Joshua Scholar's) library do not do Load_Acquire // here, since most algorithms do not require atomic loads. Needs some research. inline operator T() const { return Value; } // *** Standard Atomic inlines inline T Exchange_Sync(T val) { return Ops::Exchange_Sync(&Value, val); } inline T Exchange_Release(T val) { return Ops::Exchange_Release(&Value, val); } inline T Exchange_Acquire(T val) { return Ops::Exchange_Acquire(&Value, val); } inline T Exchange_NoSync(T val) { return Ops::Exchange_NoSync(&Value, val); } inline bool CompareAndSet_Sync(T c, T val) { return Ops::CompareAndSet_Sync(&Value, c, val); } inline bool CompareAndSet_Release(T c, T val) { return Ops::CompareAndSet_Release(&Value, c, val); } inline bool CompareAndSet_Acquire(T c, T val) { return Ops::CompareAndSet_Relse(&Value, c, val); } inline bool CompareAndSet_NoSync(T c, T val) { return Ops::CompareAndSet_NoSync(&Value, c, val); } // Load & Store. inline void Store_Release(T val) { Ops::Store_Release(&Value, val); } inline T Load_Acquire() const { return Ops::Load_Acquire(&Value); } }; // ***** AtomicPtr - Atomic pointer template // This pointer class supports atomic assignments with release, // increment / decrement operations, and conditional compare + set. template class AtomicPtr : public AtomicValueBase { typedef typename AtomicValueBase::Ops Ops; public: // Initialize pointer value to 0 by default; use Store_Release only with explicit constructor. inline AtomicPtr() : AtomicValueBase() { this->Value = 0; } explicit inline AtomicPtr(T* val) : AtomicValueBase(val) { } // Pointer access. inline T* operator -> () const { return this->Load_Acquire(); } // It looks like it is convenient to have Load_Acquire characteristics // for this, since that is convenient for algorithms such as linked // list traversals that can be added to bu another thread. inline operator T* () const { return this->Load_Acquire(); } // *** Standard Atomic inlines (applicable to pointers) // ExhangeAdd considers pointer size for pointers. template inline T* ExchangeAdd_Sync(I incr) { return Ops::ExchangeAdd_Sync(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_Release(I incr) { return Ops::ExchangeAdd_Release(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_Acquire(I incr) { return Ops::ExchangeAdd_Acquire(&this->Value, ((T*)0) + incr); } template inline T* ExchangeAdd_NoSync(I incr) { return Ops::ExchangeAdd_NoSync(&this->Value, ((T*)0) + incr); } // *** Atomic Operators inline T* operator = (T* val) { this->Store_Release(val); return val; } template inline T* operator += (I val) { return ExchangeAdd_Sync(val) + val; } template inline T* operator -= (I val) { return operator += (-val); } inline T* operator ++ () { return ExchangeAdd_Sync(1) + 1; } inline T* operator -- () { return ExchangeAdd_Sync(-1) - 1; } inline T* operator ++ (int) { return ExchangeAdd_Sync(1); } inline T* operator -- (int) { return ExchangeAdd_Sync(-1); } }; // ***** AtomicInt - Atomic integer template // Implements an atomic integer type; the exact type to use is provided // as an argument. Supports atomic Acquire / Release semantics, atomic // arithmetic operations, and atomic conditional compare + set. template class AtomicInt : public AtomicValueBase { typedef typename AtomicValueBase::Ops Ops; public: inline AtomicInt() : AtomicValueBase() { } explicit inline AtomicInt(T val) : AtomicValueBase(val) { } // *** Standard Atomic inlines (applicable to int) inline T ExchangeAdd_Sync(T val) { return Ops::ExchangeAdd_Sync(&this->Value, val); } inline T ExchangeAdd_Release(T val) { return Ops::ExchangeAdd_Release(&this->Value, val); } inline T ExchangeAdd_Acquire(T val) { return Ops::ExchangeAdd_Acquire(&this->Value, val); } inline T ExchangeAdd_NoSync(T val) { return Ops::ExchangeAdd_NoSync(&this->Value, val); } // These increments could be more efficient because they don't return a value. inline void Increment_Sync() { ExchangeAdd_Sync((T)1); } inline void Increment_Release() { ExchangeAdd_Release((T)1); } inline void Increment_Acquire() { ExchangeAdd_Acquire((T)1); } inline void Increment_NoSync() { ExchangeAdd_NoSync((T)1); } // *** Atomic Operators inline T operator = (T val) { this->Store_Release(val); return val; } inline T operator += (T val) { return ExchangeAdd_Sync(val) + val; } inline T operator -= (T val) { return ExchangeAdd_Sync(0 - val) - val; } inline T operator ++ () { return ExchangeAdd_Sync((T)1) + 1; } inline T operator -- () { return ExchangeAdd_Sync(((T)0)-1) - 1; } inline T operator ++ (int) { return ExchangeAdd_Sync((T)1); } inline T operator -- (int) { return ExchangeAdd_Sync(((T)0)-1); } // More complex atomic operations. Leave it to compiler whether to optimize them or not. T operator &= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp & arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator |= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp | arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator ^= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp ^ arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator *= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp * arg; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } T operator /= (T arg) { T comp, newVal; do { comp = this->Value; newVal = comp / arg; } while(!CompareAndSet_Sync(comp, newVal)); return newVal; } T operator >>= (unsigned bits) { T comp, newVal; do { comp = this->Value; newVal = comp >> bits; } while(!CompareAndSet_Sync(comp, newVal)); return newVal; } T operator <<= (unsigned bits) { T comp, newVal; do { comp = this->Value; newVal = comp << bits; } while(!this->CompareAndSet_Sync(comp, newVal)); return newVal; } }; //----------------------------------------------------------------------------------- // ***** Lock // Lock is a simplest and most efficient mutual-exclusion lock class. // Unlike Mutex, it cannot be waited on. class Lock { // NOTE: Locks are not allocatable and they themselves should not allocate // memory by standard means. This is the case because StandardAllocator // relies on this class. // Make 'delete' private. Don't do this for 'new' since it can be redefined. void operator delete(void*) {} // *** Lock implementation for various platforms. #if !defined(OVR_ENABLE_THREADS) public: // With no thread support, lock does nothing. inline Lock() { } inline Lock(unsigned) { } inline ~Lock() { } inline void DoLock() { } inline void Unlock() { } // Windows. #elif defined(OVR_OS_WIN32) CRITICAL_SECTION cs; public: Lock(unsigned spinCount = 0); ~Lock(); // Locking functions. inline void DoLock() { ::EnterCriticalSection(&cs); } inline void Unlock() { ::LeaveCriticalSection(&cs); } #else pthread_mutex_t mutex; public: static pthread_mutexattr_t RecursiveAttr; static bool RecursiveAttrInit; Lock (unsigned dummy = 0) { OVR_UNUSED(dummy); if (!RecursiveAttrInit) { pthread_mutexattr_init(&RecursiveAttr); pthread_mutexattr_settype(&RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); RecursiveAttrInit = 1; } pthread_mutex_init(&mutex,&RecursiveAttr); } ~Lock () { pthread_mutex_destroy(&mutex); } inline void DoLock() { pthread_mutex_lock(&mutex); } inline void Unlock() { pthread_mutex_unlock(&mutex); } #endif // OVR_ENABLE_THREDS public: // Locker class, used for automatic locking class Locker { public: Lock *pLock; inline Locker(Lock *plock) { pLock = plock; pLock->DoLock(); } inline ~Locker() { pLock->Unlock(); } }; }; //------------------------------------------------------------------------------------- // Globally shared Lock implementation used for MessageHandlers, etc. class SharedLock { public: SharedLock() : UseCount(0) {} Lock* GetLockAddRef(); void ReleaseLock(Lock* plock); private: Lock* toLock() { return (Lock*)Buffer; } // UseCount and max alignment. volatile int UseCount; UInt64 Buffer[(sizeof(Lock)+sizeof(UInt64)-1)/sizeof(UInt64)]; }; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Color.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Color.h new file mode 100644 index 0000000..137f30c --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Color.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Color.h Content : Contains color struct. Created : February 7, 2013 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Color_h #define OVR_Color_h #include "OVR_Types.h" namespace OVR { struct Color { UByte R,G,B,A; Color() {} // Constructs color by channel. Alpha is set to 0xFF (fully visible) // if not specified. Color(unsigned char r,unsigned char g,unsigned char b, unsigned char a = 0xFF) : R(r), G(g), B(b), A(a) { } // 0xAARRGGBB - Common HTML color Hex layout Color(unsigned c) : R((unsigned char)(c>>16)), G((unsigned char)(c>>8)), B((unsigned char)c), A((unsigned char)(c>>24)) { } bool operator==(const Color& b) const { return R == b.R && G == b.G && B == b.B && A == b.A; } void GetRGBA(float *r, float *g, float *b, float* a) const { *r = R / 255.0f; *g = G / 255.0f; *b = B / 255.0f; *a = A / 255.0f; } }; } #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_ContainerAllocator.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_ContainerAllocator.h new file mode 100644 index 0000000..ee73437 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_ContainerAllocator.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_ContainerAllocator.h Content : Template allocators and constructors for containers. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_ContainerAllocator_h #define OVR_ContainerAllocator_h #include "OVR_Allocator.h" #include namespace OVR { //----------------------------------------------------------------------------------- // ***** Container Allocator // ContainerAllocator serves as a template argument for allocations done by // containers, such as Array and Hash; replacing it could allow allocator // substitution in containers. class ContainerAllocatorBase { public: static void* Alloc(UPInt size) { return OVR_ALLOC(size); } static void* Realloc(void* p, UPInt newSize) { return OVR_REALLOC(p, newSize); } static void Free(void *p) { OVR_FREE(p); } }; //----------------------------------------------------------------------------------- // ***** Constructors, Destructors, Copiers // Plain Old Data - movable, no special constructors/destructor. template class ConstructorPOD { public: static void Construct(void *) {} static void Construct(void *p, const T& source) { *(T*)p = source; } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void *p, const S& source) { *(T*)p = source; } static void ConstructArray(void*, UPInt) {} static void ConstructArray(void* p, UPInt count, const T& source) { UByte *pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) *(T*)pdata = source; } static void ConstructArray(void* p, UPInt count, const T* psource) { memcpy(p, psource, sizeof(T) * count); } static void Destruct(T*) {} static void DestructArray(T*, UPInt) {} static void CopyArrayForward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static bool IsMovable() { return true; } }; //----------------------------------------------------------------------------------- // ***** ConstructorMov // // Correct C++ construction and destruction for movable objects template class ConstructorMov { public: static void Construct(void* p) { OVR::Construct(p); } static void Construct(void* p, const T& source) { OVR::Construct(p, source); } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void* p, const S& source) { OVR::ConstructAlt(p, source); } static void ConstructArray(void* p, UPInt count) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata); } static void ConstructArray(void* p, UPInt count, const T& source) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, source); } static void ConstructArray(void* p, UPInt count, const T* psource) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, *psource++); } static void Destruct(T* p) { p->~T(); OVR_UNUSED(p); // Suppress silly MSVC warning } static void DestructArray(T* p, UPInt count) { p += count - 1; for (UPInt i=0; i~T(); } static void CopyArrayForward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { memmove(dst, src, count * sizeof(T)); } static bool IsMovable() { return true; } }; //----------------------------------------------------------------------------------- // ***** ConstructorCPP // // Correct C++ construction and destruction for movable objects template class ConstructorCPP { public: static void Construct(void* p) { OVR::Construct(p); } static void Construct(void* p, const T& source) { OVR::Construct(p, source); } // Same as above, but allows for a different type of constructor. template static void ConstructAlt(void* p, const S& source) { OVR::ConstructAlt(p, source); } static void ConstructArray(void* p, UPInt count) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata); } static void ConstructArray(void* p, UPInt count, const T& source) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, source); } static void ConstructArray(void* p, UPInt count, const T* psource) { UByte* pdata = (UByte*)p; for (UPInt i=0; i< count; ++i, pdata += sizeof(T)) Construct(pdata, *psource++); } static void Destruct(T* p) { p->~T(); OVR_UNUSED(p); // Suppress silly MSVC warning } static void DestructArray(T* p, UPInt count) { p += count - 1; for (UPInt i=0; i~T(); } static void CopyArrayForward(T* dst, const T* src, UPInt count) { for(UPInt i = 0; i < count; ++i) dst[i] = src[i]; } static void CopyArrayBackward(T* dst, const T* src, UPInt count) { for(UPInt i = count; i; --i) dst[i-1] = src[i-1]; } static bool IsMovable() { return false; } }; //----------------------------------------------------------------------------------- // ***** Container Allocator with movement policy // // Simple wraps as specialized allocators template struct ContainerAllocator_POD : ContainerAllocatorBase, ConstructorPOD {}; template struct ContainerAllocator : ContainerAllocatorBase, ConstructorMov {}; template struct ContainerAllocator_CPP : ContainerAllocatorBase, ConstructorCPP {}; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Deque.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Deque.h new file mode 100644 index 0000000..f264be3 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Deque.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Deque.h Content : Deque container Created : Nov. 15, 2013 Authors : Dov Katz Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Deque_h #define OVR_Deque_h namespace OVR{ template class Deque { public: enum { DefaultCapacity = 500 }; Deque(int capacity = DefaultCapacity); virtual ~Deque(void); virtual void PushBack (const Elem &Item); // Adds Item to the end virtual void PushFront (const Elem &Item); // Adds Item to the beginning virtual Elem PopBack (void); // Removes Item from the end virtual Elem PopFront (void); // Removes Item from the beginning virtual const Elem& PeekBack (int count = 0) const; // Returns count-th Item from the end virtual const Elem& PeekFront (int count = 0) const; // Returns count-th Item from the beginning virtual inline UPInt GetSize (void) const; // Returns Number of Elements virtual inline UPInt GetCapacity(void) const; // Returns the maximum possible number of elements virtual void Clear (void); // Remove all elements virtual inline bool IsEmpty () const; virtual inline bool IsFull () const; protected: Elem *Data; // The actual Data array const int Capacity; // Deque capacity int Beginning; // Index of the first element int End; // Index of the next after last element // Instead of calculating the number of elements, using this variable // is much more convenient. int ElemCount; private: Deque& operator= (const Deque& q) { }; // forbidden Deque(const Deque &OtherDeque) { }; }; template class InPlaceMutableDeque : public Deque { public: InPlaceMutableDeque( int capacity = Deque::DefaultCapacity ) : Deque( capacity ) {} virtual ~InPlaceMutableDeque() {}; using Deque::PeekBack; using Deque::PeekFront; virtual Elem& PeekBack (int count = 0); // Returns count-th Item from the end virtual Elem& PeekFront (int count = 0); // Returns count-th Item from the beginning }; // Same as Deque, but allows to write more elements than maximum capacity // Old elements are lost as they are overwritten with the new ones template class CircularBuffer : public InPlaceMutableDeque { public: CircularBuffer(int MaxSize = Deque::DefaultCapacity) : InPlaceMutableDeque(MaxSize) { }; // The following methods are inline as a workaround for a VS bug causing erroneous C4505 warnings // See: http://stackoverflow.com/questions/3051992/compiler-warning-at-c-template-base-class inline virtual void PushBack (const Elem &Item); // Adds Item to the end, overwriting the oldest element at the beginning if necessary inline virtual void PushFront (const Elem &Item); // Adds Item to the beginning, overwriting the oldest element at the end if necessary }; //---------------------------------------------------------------------------------- // Deque Constructor function template Deque::Deque(int capacity) : Capacity( capacity ), Beginning(0), End(0), ElemCount(0) { Data = (Elem*) OVR_ALLOC(Capacity * sizeof(Elem)); ConstructArray(Data, Capacity); } // Deque Destructor function template Deque::~Deque(void) { DestructArray(Data, Capacity); OVR_FREE(Data); } template void Deque::Clear() { Beginning = 0; End = 0; ElemCount = 0; DestructArray(Data, Capacity); ConstructArray(Data, Capacity); } // Push functions template void Deque::PushBack(const Elem &Item) { // Error Check: Make sure we aren't // exceeding our maximum storage space OVR_ASSERT( ElemCount < Capacity ); Data[ End++ ] = Item; ++ElemCount; // Check for wrap-around if (End >= Capacity) End -= Capacity; } template void Deque::PushFront(const Elem &Item) { // Error Check: Make sure we aren't // exceeding our maximum storage space OVR_ASSERT( ElemCount < Capacity ); Beginning--; // Check for wrap-around if (Beginning < 0) Beginning += Capacity; Data[ Beginning ] = Item; ++ElemCount; } // Pop functions template Elem Deque::PopFront(void) { // Error Check: Make sure we aren't reading from an empty Deque OVR_ASSERT( ElemCount > 0 ); Elem ReturnValue = Data[ Beginning ]; Destruct(&Data[ Beginning ]); Construct(&Data[ Beginning ]); ++Beginning; --ElemCount; // Check for wrap-around if (Beginning >= Capacity) Beginning -= Capacity; return ReturnValue; } template Elem Deque::PopBack(void) { // Error Check: Make sure we aren't reading from an empty Deque OVR_ASSERT( ElemCount > 0 ); End--; --ElemCount; // Check for wrap-around if (End < 0) End += Capacity; Elem ReturnValue = Data[ End ]; Destruct(&Data[ End ]); Construct(&Data[ End ]); return ReturnValue; } // Peek functions template const Elem& Deque::PeekFront(int count) const { // Error Check: Make sure we aren't reading from an empty Deque OVR_ASSERT( ElemCount > count ); int idx = Beginning + count; if (idx >= Capacity) idx -= Capacity; return Data[ idx ]; } template const Elem& Deque::PeekBack(int count) const { // Error Check: Make sure we aren't reading from an empty Deque OVR_ASSERT( ElemCount > count ); int idx = End - count - 1; if (idx < 0) idx += Capacity; return Data[ idx ]; } // Mutable Peek functions template Elem& InPlaceMutableDeque::PeekFront(int count) { // Error Check: Make sure we aren't reading from an empty Deque OVR_ASSERT( Deque::ElemCount > count ); int idx = Deque::Beginning + count; if (idx >= Deque::Capacity) idx -= Deque::Capacity; return Deque::Data[ idx ]; } template Elem& InPlaceMutableDeque::PeekBack(int count) { // Error Check: Make sure we aren't reading from an empty Deque OVR_ASSERT( Deque::ElemCount > count ); int idx = Deque::End - count - 1; if (idx < 0) idx += Deque::Capacity; return Deque::Data[ idx ]; } template inline UPInt Deque::GetCapacity(void) const { return Deque::Capacity; } template inline UPInt Deque::GetSize(void) const { return Deque::ElemCount; } template inline bool Deque::IsEmpty(void) const { return Deque::ElemCount==0; } template inline bool Deque::IsFull(void) const { return Deque::ElemCount==Deque::Capacity; } // ******* CircularBuffer ******* // Push functions template void CircularBuffer::PushBack(const Elem &Item) { if (this->IsFull()) this->PopFront(); Deque::PushBack(Item); } template void CircularBuffer::PushFront(const Elem &Item) { if (this->IsFull()) this->PopBack(); Deque::PushFront(Item); } }; #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_File.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_File.cpp new file mode 100644 index 0000000..1e0132e --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_File.cpp @@ -0,0 +1 @@ +/************************************************************************** Filename : OVR_File.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. **************************************************************************/ #define GFILE_CXX // Standard C library (Captain Obvious guarantees!) #include #include "OVR_File.h" namespace OVR { // Buffered file adds buffering to an existing file // FILEBUFFER_SIZE defines the size of internal buffer, while // FILEBUFFER_TOLERANCE controls the amount of data we'll effectively try to buffer #define FILEBUFFER_SIZE (8192-8) #define FILEBUFFER_TOLERANCE 4096 // ** Constructor/Destructor // Hidden constructor // Not supposed to be used BufferedFile::BufferedFile() : DelegatedFile(0) { pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); BufferMode = NoBuffer; FilePos = 0; Pos = 0; DataSize = 0; } // Takes another file as source BufferedFile::BufferedFile(File *pfile) : DelegatedFile(pfile) { pBuffer = (UByte*)OVR_ALLOC(FILEBUFFER_SIZE); BufferMode = NoBuffer; FilePos = pfile->LTell(); Pos = 0; DataSize = 0; } // Destructor BufferedFile::~BufferedFile() { // Flush in case there's data if (pFile) FlushBuffer(); // Get rid of buffer if (pBuffer) OVR_FREE(pBuffer); } /* bool BufferedFile::VCopy(const Object &source) { if (!DelegatedFile::VCopy(source)) return 0; // Data members BufferedFile *psource = (BufferedFile*)&source; // Buffer & the mode it's in pBuffer = psource->pBuffer; BufferMode = psource->BufferMode; Pos = psource->Pos; DataSize = psource->DataSize; return 1; } */ // Initializes buffering to a certain mode bool BufferedFile::SetBufferMode(BufferModeType mode) { if (!pBuffer) return false; if (mode == BufferMode) return true; FlushBuffer(); // Can't set write mode if we can't write if ((mode==WriteBuffer) && (!pFile || !pFile->IsWritable()) ) return 0; // And SetMode BufferMode = mode; Pos = 0; DataSize = 0; return 1; } // Flushes buffer void BufferedFile::FlushBuffer() { switch(BufferMode) { case WriteBuffer: // Write data in buffer FilePos += pFile->Write(pBuffer,Pos); Pos = 0; break; case ReadBuffer: // Seek back & reset buffer data if ((DataSize-Pos)>0) FilePos = pFile->LSeek(-(int)(DataSize-Pos), Seek_Cur); DataSize = 0; Pos = 0; break; default: // not handled! break; } } // Reloads data for ReadBuffer void BufferedFile::LoadBuffer() { if (BufferMode == ReadBuffer) { // We should only reload once all of pre-loaded buffer is consumed. OVR_ASSERT(Pos == DataSize); // WARNING: Right now LoadBuffer() assumes the buffer's empty int sz = pFile->Read(pBuffer,FILEBUFFER_SIZE); DataSize = sz<0 ? 0 : (unsigned)sz; Pos = 0; FilePos += DataSize; } } // ** Overridden functions // We override all the functions that can possibly // require buffer mode switch, flush, or extra calculations // Tell() requires buffer adjustment int BufferedFile::Tell() { if (BufferMode == ReadBuffer) return int (FilePos - DataSize + Pos); int pos = pFile->Tell(); // Adjust position based on buffer mode & data if (pos!=-1) { OVR_ASSERT(BufferMode != ReadBuffer); if (BufferMode == WriteBuffer) pos += Pos; } return pos; } SInt64 BufferedFile::LTell() { if (BufferMode == ReadBuffer) return FilePos - DataSize + Pos; SInt64 pos = pFile->LTell(); if (pos!=-1) { OVR_ASSERT(BufferMode != ReadBuffer); if (BufferMode == WriteBuffer) pos += Pos; } return pos; } int BufferedFile::GetLength() { int len = pFile->GetLength(); // If writing through buffer, file length may actually be bigger if ((len!=-1) && (BufferMode==WriteBuffer)) { int currPos = pFile->Tell() + Pos; if (currPos>len) len = currPos; } return len; } SInt64 BufferedFile::LGetLength() { SInt64 len = pFile->LGetLength(); // If writing through buffer, file length may actually be bigger if ((len!=-1) && (BufferMode==WriteBuffer)) { SInt64 currPos = pFile->LTell() + Pos; if (currPos>len) len = currPos; } return len; } /* bool BufferedFile::Stat(FileStats *pfs) { // Have to fix up length is stat if (pFile->Stat(pfs)) { if (BufferMode==WriteBuffer) { SInt64 currPos = pFile->LTell() + Pos; if (currPos > pfs->Size) { pfs->Size = currPos; // ?? pfs->Blocks = (pfs->Size+511) >> 9; } } return 1; } return 0; } */ int BufferedFile::Write(const UByte *psourceBuffer, int numBytes) { if ( (BufferMode==WriteBuffer) || SetBufferMode(WriteBuffer)) { // If not data space in buffer, flush if ((FILEBUFFER_SIZE-(int)Pos)FILEBUFFER_TOLERANCE) { int sz = pFile->Write(psourceBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } } // Enough space in buffer.. so copy to it memcpy(pBuffer+Pos, psourceBuffer, numBytes); Pos += numBytes; return numBytes; } int sz = pFile->Write(psourceBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } int BufferedFile::Read(UByte *pdestBuffer, int numBytes) { if ( (BufferMode==ReadBuffer) || SetBufferMode(ReadBuffer)) { // Data in buffer... copy it if ((int)(DataSize-Pos) >= numBytes) { memcpy(pdestBuffer, pBuffer+Pos, numBytes); Pos += numBytes; return numBytes; } // Not enough data in buffer, copy buffer int readBytes = DataSize-Pos; memcpy(pdestBuffer, pBuffer+Pos, readBytes); numBytes -= readBytes; pdestBuffer += readBytes; Pos = DataSize; // Don't reload buffer if more then tolerance // (No major advantage, and we don't want to write a loop) if (numBytes>FILEBUFFER_TOLERANCE) { numBytes = pFile->Read(pdestBuffer,numBytes); if (numBytes > 0) { FilePos += numBytes; Pos = DataSize = 0; } return readBytes + ((numBytes==-1) ? 0 : numBytes); } // Reload the buffer // WARNING: Right now LoadBuffer() assumes the buffer's empty LoadBuffer(); if ((int)(DataSize-Pos) < numBytes) numBytes = (int)DataSize-Pos; memcpy(pdestBuffer, pBuffer+Pos, numBytes); Pos += numBytes; return numBytes + readBytes; /* // Alternative Read implementation. The one above is probably better // due to FILEBUFFER_TOLERANCE. int total = 0; do { int bufferBytes = (int)(DataSize-Pos); int copyBytes = (bufferBytes > numBytes) ? numBytes : bufferBytes; memcpy(pdestBuffer, pBuffer+Pos, copyBytes); numBytes -= copyBytes; pdestBuffer += copyBytes; Pos += copyBytes; total += copyBytes; if (numBytes == 0) break; LoadBuffer(); } while (DataSize > 0); return total; */ } int sz = pFile->Read(pdestBuffer,numBytes); if (sz > 0) FilePos += sz; return sz; } int BufferedFile::SkipBytes(int numBytes) { int skippedBytes = 0; // Special case for skipping a little data in read buffer if (BufferMode==ReadBuffer) { skippedBytes = (((int)DataSize-(int)Pos) >= numBytes) ? numBytes : (DataSize-Pos); Pos += skippedBytes; numBytes -= skippedBytes; } if (numBytes) { numBytes = pFile->SkipBytes(numBytes); // Make sure we return the actual number skipped, or error if (numBytes!=-1) { skippedBytes += numBytes; FilePos += numBytes; Pos = DataSize = 0; } else if (skippedBytes <= 0) skippedBytes = -1; } return skippedBytes; } int BufferedFile::BytesAvailable() { int available = pFile->BytesAvailable(); // Adjust available size based on buffers switch(BufferMode) { case ReadBuffer: available += DataSize-Pos; break; case WriteBuffer: available -= Pos; if (available<0) available= 0; break; default: break; } return available; } bool BufferedFile::Flush() { FlushBuffer(); return pFile->Flush(); } // Seeking could be optimized better.. int BufferedFile::Seek(int offset, int origin) { if (BufferMode == ReadBuffer) { if (origin == Seek_Cur) { // Seek can fall either before or after Pos in the buffer, // but it must be within bounds. if (((unsigned(offset) + Pos)) <= DataSize) { Pos += offset; return int (FilePos - DataSize + Pos); } // Lightweight buffer "Flush". We do this to avoid an extra seek // back operation which would take place if we called FlushBuffer directly. origin = Seek_Set; OVR_ASSERT(((FilePos - DataSize + Pos) + (UInt64)offset) < ~(UInt64)0); offset = (int)(FilePos - DataSize + Pos) + offset; Pos = DataSize = 0; } else if (origin == Seek_Set) { if (((unsigned)offset - (FilePos-DataSize)) <= DataSize) { OVR_ASSERT((FilePos-DataSize) < ~(UInt64)0); Pos = (unsigned)offset - (unsigned)(FilePos-DataSize); return offset; } Pos = DataSize = 0; } else { FlushBuffer(); } } else { FlushBuffer(); } /* // Old Seek Logic if (origin == Seek_Cur && offset + Pos < DataSize) { //OVR_ASSERT((FilePos - DataSize) >= (FilePos - DataSize + Pos + offset)); Pos += offset; OVR_ASSERT(int (Pos) >= 0); return int (FilePos - DataSize + Pos); } else if (origin == Seek_Set && unsigned(offset) >= FilePos - DataSize && unsigned(offset) < FilePos) { Pos = unsigned(offset - FilePos + DataSize); OVR_ASSERT(int (Pos) >= 0); return int (FilePos - DataSize + Pos); } FlushBuffer(); */ FilePos = pFile->Seek(offset,origin); return int (FilePos); } SInt64 BufferedFile::LSeek(SInt64 offset, int origin) { if (BufferMode == ReadBuffer) { if (origin == Seek_Cur) { // Seek can fall either before or after Pos in the buffer, // but it must be within bounds. if (((unsigned(offset) + Pos)) <= DataSize) { Pos += (unsigned)offset; return SInt64(FilePos - DataSize + Pos); } // Lightweight buffer "Flush". We do this to avoid an extra seek // back operation which would take place if we called FlushBuffer directly. origin = Seek_Set; offset = (SInt64)(FilePos - DataSize + Pos) + offset; Pos = DataSize = 0; } else if (origin == Seek_Set) { if (((UInt64)offset - (FilePos-DataSize)) <= DataSize) { Pos = (unsigned)((UInt64)offset - (FilePos-DataSize)); return offset; } Pos = DataSize = 0; } else { FlushBuffer(); } } else { FlushBuffer(); } /* OVR_ASSERT(BufferMode != NoBuffer); if (origin == Seek_Cur && offset + Pos < DataSize) { Pos += int (offset); return FilePos - DataSize + Pos; } else if (origin == Seek_Set && offset >= SInt64(FilePos - DataSize) && offset < SInt64(FilePos)) { Pos = unsigned(offset - FilePos + DataSize); return FilePos - DataSize + Pos; } FlushBuffer(); */ FilePos = pFile->LSeek(offset,origin); return FilePos; } int BufferedFile::CopyFromStream(File *pstream, int byteSize) { // We can't rely on overridden Write() // because delegation doesn't override virtual pointers // So, just re-implement UByte buff[0x4000]; int count = 0; int szRequest, szRead, szWritten; while(byteSize) { szRequest = (byteSize > int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; szRead = pstream->Read(buff,szRequest); szWritten = 0; if (szRead > 0) szWritten = Write(buff,szRead); count +=szWritten; byteSize-=szWritten; if (szWritten < szRequest) break; } return count; } // Closing files bool BufferedFile::Close() { switch(BufferMode) { case WriteBuffer: FlushBuffer(); break; case ReadBuffer: // No need to seek back on close BufferMode = NoBuffer; break; default: break; } return pFile->Close(); } // ***** Global path helpers // Find trailing short filename in a path. const char* OVR_CDECL GetShortFilename(const char* purl) { UPInt len = OVR_strlen(purl); for (UPInt i=len; i>0; i--) if (purl[i]=='\\' || purl[i]=='/') return purl+i+1; return purl; } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_File.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_File.h new file mode 100644 index 0000000..c793791 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_File.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: Kernel Filename : OVR_File.h Content : Header for all internal file management - functions and structures to be inherited by OS specific subclasses. Created : September 19, 2012 Notes : Notes : errno may not be preserved across use of BaseFile member functions : Directories cannot be deleted while files opened from them are in use (For the GetFullName function) Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_File_h #define OVR_File_h #include "OVR_RefCount.h" #include "OVR_Std.h" #include "OVR_Alg.h" #include #include "OVR_String.h" namespace OVR { // ***** Declared classes class FileConstants; class File; class DelegatedFile; class BufferedFile; // ***** Flags for File & Directory accesses class FileConstants { public: // *** File open flags enum OpenFlags { Open_Read = 1, Open_Write = 2, Open_ReadWrite = 3, // Opens file and truncates it to zero length // - file must have write permission // - when used with Create, it opens an existing // file and empties it or creates a new file Open_Truncate = 4, // Creates and opens new file // - does not erase contents if file already // exists unless combined with Truncate Open_Create = 8, // Returns an error value if the file already exists Open_CreateOnly = 24, // Open file with buffering Open_Buffered = 32 }; // *** File Mode flags enum Modes { Mode_Read = 0444, Mode_Write = 0222, Mode_Execute = 0111, Mode_ReadWrite = 0666 }; // *** Seek operations enum SeekOps { Seek_Set = 0, Seek_Cur = 1, Seek_End = 2 }; // *** Errors enum Errors { Error_FileNotFound = 0x1001, Error_Access = 0x1002, Error_IOError = 0x1003, Error_DiskFull = 0x1004 }; }; //----------------------------------------------------------------------------------- // ***** File Class // The pure virtual base random-access file // This is a base class to all files class File : public RefCountBase, public FileConstants { public: File() { } // ** Location Information // Returns a file name path relative to the 'reference' directory // This is often a path that was used to create a file // (this is not a global path, global path can be obtained with help of directory) virtual const char* GetFilePath() = 0; // ** File Information // Return 1 if file's usable (open) virtual bool IsValid() = 0; // Return 1 if file's writable, otherwise 0 virtual bool IsWritable() = 0; // Return position virtual int Tell() = 0; virtual SInt64 LTell() = 0; // File size virtual int GetLength() = 0; virtual SInt64 LGetLength() = 0; // Returns file stats // 0 for failure //virtual bool Stat(FileStats *pfs) = 0; // Return errno-based error code // Useful if any other function failed virtual int GetErrorCode() = 0; // ** Stream implementation & I/O // Blocking write, will write in the given number of bytes to the stream // Returns : -1 for error // Otherwise number of bytes read virtual int Write(const UByte *pbufer, int numBytes) = 0; // Blocking read, will read in the given number of bytes or less from the stream // Returns : -1 for error // Otherwise number of bytes read, // if 0 or < numBytes, no more bytes available; end of file or the other side of stream is closed virtual int Read(UByte *pbufer, int numBytes) = 0; // Skips (ignores) a given # of bytes // Same return values as Read virtual int SkipBytes(int numBytes) = 0; // Returns the number of bytes available to read from a stream without blocking // For a file, this should generally be number of bytes to the end virtual int BytesAvailable() = 0; // Causes any implementation's buffered data to be delivered to destination // Return 0 for error virtual bool Flush() = 0; // Need to provide a more optimized implementation that doe snot necessarily involve a lot of seeking inline bool IsEOF() { return !BytesAvailable(); } // Seeking // Returns new position, -1 for error virtual int Seek(int offset, int origin=Seek_Set) = 0; virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) = 0; // Seek simplification int SeekToBegin() {return Seek(0); } int SeekToEnd() {return Seek(0,Seek_End); } int Skip(int numBytes) {return Seek(numBytes,Seek_Cur); } // Appends other file data from a stream // Return -1 for error, else # of bytes written virtual int CopyFromStream(File *pstream, int byteSize) = 0; // Closes the file // After close, file cannot be accessed virtual bool Close() = 0; // ***** Inlines for convenient primitive type serialization // Read/Write helpers private: UInt64 PRead64() { UInt64 v = 0; Read((UByte*)&v, 8); return v; } UInt32 PRead32() { UInt32 v = 0; Read((UByte*)&v, 4); return v; } UInt16 PRead16() { UInt16 v = 0; Read((UByte*)&v, 2); return v; } UByte PRead8() { UByte v = 0; Read((UByte*)&v, 1); return v; } void PWrite64(UInt64 v) { Write((UByte*)&v, 8); } void PWrite32(UInt32 v) { Write((UByte*)&v, 4); } void PWrite16(UInt16 v) { Write((UByte*)&v, 2); } void PWrite8(UByte v) { Write((UByte*)&v, 1); } public: // Writing primitive types - Little Endian inline void WriteUByte(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSByte(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt8(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt8(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt16(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt16(SInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt32(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt32(SInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToLE(v)); } inline void WriteUInt64(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } inline void WriteSInt64(SInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToLE(v)); } inline void WriteFloat(float v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 4); } inline void WriteDouble(double v) { v = Alg::ByteUtil::SystemToLE(v); Write((UByte*)&v, 8); } // Writing primitive types - Big Endian inline void WriteUByteBE(UByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSByteBE(SByte v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt8BE(UInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt8BE(SInt16 v) { PWrite8((UByte)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt16BE(UInt16 v) { PWrite16((UInt16)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt32BE(UInt32 v) { PWrite32((UInt32)Alg::ByteUtil::SystemToBE(v)); } inline void WriteUInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } inline void WriteSInt64BE(UInt64 v) { PWrite64((UInt64)Alg::ByteUtil::SystemToBE(v)); } inline void WriteFloatBE(float v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 4); } inline void WriteDoubleBE(double v) { v = Alg::ByteUtil::SystemToBE(v); Write((UByte*)&v, 8); } // Reading primitive types - Little Endian inline UByte ReadUByte() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline SByte ReadSByte() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline UByte ReadUInt8() { return (UByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline SByte ReadSInt8() { return (SByte)Alg::ByteUtil::LEToSystem(PRead8()); } inline UInt16 ReadUInt16() { return (UInt16)Alg::ByteUtil::LEToSystem(PRead16()); } inline SInt16 ReadSInt16() { return (SInt16)Alg::ByteUtil::LEToSystem(PRead16()); } inline UInt32 ReadUInt32() { return (UInt32)Alg::ByteUtil::LEToSystem(PRead32()); } inline SInt32 ReadSInt32() { return (SInt32)Alg::ByteUtil::LEToSystem(PRead32()); } inline UInt64 ReadUInt64() { return (UInt64)Alg::ByteUtil::LEToSystem(PRead64()); } inline SInt64 ReadSInt64() { return (SInt64)Alg::ByteUtil::LEToSystem(PRead64()); } inline float ReadFloat() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::LEToSystem(v); } inline double ReadDouble() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::LEToSystem(v); } // Reading primitive types - Big Endian inline UByte ReadUByteBE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline SByte ReadSByteBE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline UByte ReadUInt8BE() { return (UByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline SByte ReadSInt8BE() { return (SByte)Alg::ByteUtil::BEToSystem(PRead8()); } inline UInt16 ReadUInt16BE() { return (UInt16)Alg::ByteUtil::BEToSystem(PRead16()); } inline SInt16 ReadSInt16BE() { return (SInt16)Alg::ByteUtil::BEToSystem(PRead16()); } inline UInt32 ReadUInt32BE() { return (UInt32)Alg::ByteUtil::BEToSystem(PRead32()); } inline SInt32 ReadSInt32BE() { return (SInt32)Alg::ByteUtil::BEToSystem(PRead32()); } inline UInt64 ReadUInt64BE() { return (UInt64)Alg::ByteUtil::BEToSystem(PRead64()); } inline SInt64 ReadSInt64BE() { return (SInt64)Alg::ByteUtil::BEToSystem(PRead64()); } inline float ReadFloatBE() { float v = 0.0f; Read((UByte*)&v, 4); return Alg::ByteUtil::BEToSystem(v); } inline double ReadDoubleBE() { double v = 0.0; Read((UByte*)&v, 8); return Alg::ByteUtil::BEToSystem(v); } }; // *** Delegated File class DelegatedFile : public File { protected: // Delegating file pointer Ptr pFile; // Hidden default constructor DelegatedFile() : pFile(0) { } DelegatedFile(const DelegatedFile &source) : File() { OVR_UNUSED(source); } public: // Constructors DelegatedFile(File *pfile) : pFile(pfile) { } // ** Location Information virtual const char* GetFilePath() { return pFile->GetFilePath(); } // ** File Information virtual bool IsValid() { return pFile && pFile->IsValid(); } virtual bool IsWritable() { return pFile->IsWritable(); } // virtual bool IsRecoverable() { return pFile->IsRecoverable(); } virtual int Tell() { return pFile->Tell(); } virtual SInt64 LTell() { return pFile->LTell(); } virtual int GetLength() { return pFile->GetLength(); } virtual SInt64 LGetLength() { return pFile->LGetLength(); } //virtual bool Stat(FileStats *pfs) { return pFile->Stat(pfs); } virtual int GetErrorCode() { return pFile->GetErrorCode(); } // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes) { return pFile->Write(pbuffer,numBytes); } virtual int Read(UByte *pbuffer, int numBytes) { return pFile->Read(pbuffer,numBytes); } virtual int SkipBytes(int numBytes) { return pFile->SkipBytes(numBytes); } virtual int BytesAvailable() { return pFile->BytesAvailable(); } virtual bool Flush() { return pFile->Flush(); } // Seeking virtual int Seek(int offset, int origin=Seek_Set) { return pFile->Seek(offset,origin); } virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set) { return pFile->LSeek(offset,origin); } virtual int CopyFromStream(File *pstream, int byteSize) { return pFile->CopyFromStream(pstream,byteSize); } // Closing the file virtual bool Close() { return pFile->Close(); } }; //----------------------------------------------------------------------------------- // ***** Buffered File // This file class adds buffering to an existing file // Buffered file never fails by itself; if there's not // enough memory for buffer, no buffer's used class BufferedFile : public DelegatedFile { protected: enum BufferModeType { NoBuffer, ReadBuffer, WriteBuffer }; // Buffer & the mode it's in UByte* pBuffer; BufferModeType BufferMode; // Position in buffer unsigned Pos; // Data in buffer if reading unsigned DataSize; // Underlying file position UInt64 FilePos; // Initializes buffering to a certain mode bool SetBufferMode(BufferModeType mode); // Flushes buffer // WriteBuffer - write data to disk, ReadBuffer - reset buffer & fix file position void FlushBuffer(); // Loads data into ReadBuffer // WARNING: Right now LoadBuffer() assumes the buffer's empty void LoadBuffer(); // Hidden constructor BufferedFile(); inline BufferedFile(const BufferedFile &source) : DelegatedFile() { OVR_UNUSED(source); } public: // Constructor // - takes another file as source BufferedFile(File *pfile); ~BufferedFile(); // ** Overridden functions // We override all the functions that can possibly // require buffer mode switch, flush, or extra calculations virtual int Tell(); virtual SInt64 LTell(); virtual int GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(GFileStats *pfs); virtual int Write(const UByte *pbufer, int numBytes); virtual int Read(UByte *pbufer, int numBytes); virtual int SkipBytes(int numBytes); virtual int BytesAvailable(); virtual bool Flush(); virtual int Seek(int offset, int origin=Seek_Set); virtual SInt64 LSeek(SInt64 offset, int origin=Seek_Set); virtual int CopyFromStream(File *pstream, int byteSize); virtual bool Close(); }; //----------------------------------------------------------------------------------- // ***** Memory File class MemoryFile : public File { public: const char* GetFilePath() { return FilePath.ToCStr(); } bool IsValid() { return Valid; } bool IsWritable() { return false; } bool Flush() { return true; } int GetErrorCode() { return 0; } int Tell() { return FileIndex; } SInt64 LTell() { return (SInt64) FileIndex; } int GetLength() { return FileSize; } SInt64 LGetLength() { return (SInt64) FileSize; } bool Close() { Valid = false; return false; } int CopyFromStream(File *pstream, int byteSize) { OVR_UNUSED2(pstream, byteSize); return 0; } int Write(const UByte *pbuffer, int numBytes) { OVR_UNUSED2(pbuffer, numBytes); return 0; } int Read(UByte *pbufer, int numBytes) { if (FileIndex + numBytes > FileSize) { numBytes = FileSize - FileIndex; } if (numBytes > 0) { ::memcpy (pbufer, &FileData [FileIndex], numBytes); FileIndex += numBytes; } return numBytes; } int SkipBytes(int numBytes) { if (FileIndex + numBytes > FileSize) { numBytes = FileSize - FileIndex; } FileIndex += numBytes; return numBytes; } int BytesAvailable() { return (FileSize - FileIndex); } int Seek(int offset, int origin = Seek_Set) { switch (origin) { case Seek_Set : FileIndex = offset; break; case Seek_Cur : FileIndex += offset; break; case Seek_End : FileIndex = FileSize - offset; break; } return FileIndex; } SInt64 LSeek(SInt64 offset, int origin = Seek_Set) { return (SInt64) Seek((int) offset, origin); } public: MemoryFile (const String& fileName, const UByte *pBuffer, int buffSize) : FilePath(fileName) { FileData = pBuffer; FileSize = buffSize; FileIndex = 0; Valid = (!fileName.IsEmpty() && pBuffer && buffSize > 0) ? true : false; } // pfileName should be encoded as UTF-8 to support international file names. MemoryFile (const char* pfileName, const UByte *pBuffer, int buffSize) : FilePath(pfileName) { FileData = pBuffer; FileSize = buffSize; FileIndex = 0; Valid = (pfileName && pBuffer && buffSize > 0) ? true : false; } private: String FilePath; const UByte *FileData; int FileSize; int FileIndex; bool Valid; }; // ***** Global path helpers // Find trailing short filename in a path. const char* OVR_CDECL GetShortFilename(const char* purl); } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_FileFILE.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_FileFILE.cpp new file mode 100644 index 0000000..461e39e --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_FileFILE.cpp @@ -0,0 +1 @@ +/************************************************************************** Filename : OVR_FileFILE.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. **************************************************************************/ #define GFILE_CXX #include "OVR_Types.h" #include "OVR_Log.h" // Standard C library (Captain Obvious guarantees!) #include #ifndef OVR_OS_WINCE #include #endif #include "OVR_SysFile.h" #ifndef OVR_OS_WINCE #include #endif namespace OVR { // ***** File interface // ***** FILEFile - C streams file static int SFerror () { if (errno == ENOENT) return FileConstants::Error_FileNotFound; else if (errno == EACCES || errno == EPERM) return FileConstants::Error_Access; else if (errno == ENOSPC) return FileConstants::Error_DiskFull; else return FileConstants::Error_IOError; }; #ifdef OVR_OS_WIN32 #include "windows.h" // A simple helper class to disable/enable system error mode, if necessary // Disabling happens conditionally only if a drive name is involved class SysErrorModeDisabler { BOOL Disabled; UINT OldMode; public: SysErrorModeDisabler(const char* pfileName) { if (pfileName && (pfileName[0]!=0) && pfileName[1]==':') { Disabled = 1; OldMode = ::SetErrorMode(SEM_FAILCRITICALERRORS); } else Disabled = 0; } ~SysErrorModeDisabler() { if (Disabled) ::SetErrorMode(OldMode); } }; #else class SysErrorModeDisabler { public: SysErrorModeDisabler(const char* pfileName) { OVR_UNUSED(pfileName); } }; #endif // OVR_OS_WIN32 // This macro enables verification of I/O results after seeks against a pre-loaded // full file buffer copy. This is generally not necessary, but can been used to debug // memory corruptions; we've seen this fail due to EAX2/DirectSound corrupting memory // under FMOD with XP64 (32-bit) and Realtek HA Audio driver. //#define GFILE_VERIFY_SEEK_ERRORS // This is the simplest possible file implementation, it wraps around the descriptor // This file is delegated to by SysFile. class FILEFile : public File { protected: // Allocated filename String FileName; // File handle & open mode bool Opened; FILE* fs; int OpenFlags; // Error code for last request int ErrorCode; int LastOp; #ifdef OVR_FILE_VERIFY_SEEK_ERRORS UByte* pFileTestBuffer; unsigned FileTestLength; unsigned TestPos; // File pointer position during tests. #endif public: FILEFile() { Opened = 0; FileName = ""; #ifdef OVR_FILE_VERIFY_SEEK_ERRORS pFileTestBuffer =0; FileTestLength =0; TestPos =0; #endif } // Initialize file by opening it FILEFile(const String& fileName, int flags, int Mode); // The 'pfileName' should be encoded as UTF-8 to support international file names. FILEFile(const char* pfileName, int flags, int Mode); ~FILEFile() { if (Opened) Close(); } virtual const char* GetFilePath(); // ** File Information virtual bool IsValid(); virtual bool IsWritable(); // Return position / file size virtual int Tell(); virtual SInt64 LTell(); virtual int GetLength(); virtual SInt64 LGetLength(); // virtual bool Stat(FileStats *pfs); virtual int GetErrorCode(); // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes); virtual int Read(UByte *pbuffer, int numBytes); virtual int SkipBytes(int numBytes); virtual int BytesAvailable(); virtual bool Flush(); virtual int Seek(int offset, int origin); virtual SInt64 LSeek(SInt64 offset, int origin); virtual int CopyFromStream(File *pStream, int byteSize); virtual bool Close(); private: void init(); }; // Initialize file by opening it FILEFile::FILEFile(const String& fileName, int flags, int mode) : FileName(fileName), OpenFlags(flags) { OVR_UNUSED(mode); init(); } // The 'pfileName' should be encoded as UTF-8 to support international file names. FILEFile::FILEFile(const char* pfileName, int flags, int mode) : FileName(pfileName), OpenFlags(flags) { OVR_UNUSED(mode); init(); } void FILEFile::init() { // Open mode for file's open const char *omode = "rb"; if (OpenFlags & Open_Truncate) { if (OpenFlags & Open_Read) omode = "w+b"; else omode = "wb"; } else if (OpenFlags & Open_Create) { if (OpenFlags & Open_Read) omode = "a+b"; else omode = "ab"; } else if (OpenFlags & Open_Write) omode = "r+b"; #ifdef OVR_OS_WIN32 SysErrorModeDisabler disabler(FileName.ToCStr()); #endif #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) wchar_t womode[16]; wchar_t *pwFileName = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(FileName.ToCStr())+1) * sizeof(wchar_t)); UTF8Util::DecodeString(pwFileName, FileName.ToCStr()); OVR_ASSERT(strlen(omode) < sizeof(womode)/sizeof(womode[0])); UTF8Util::DecodeString(womode, omode); _wfopen_s(&fs, pwFileName, womode); OVR_FREE(pwFileName); #else fs = fopen(FileName.ToCStr(), omode); #endif if (fs) rewind (fs); Opened = (fs != NULL); // Set error code if (!Opened) ErrorCode = SFerror(); else { // If we are testing file seek correctness, pre-load the entire file so // that we can do comparison tests later. #ifdef OVR_FILE_VERIFY_SEEK_ERRORS TestPos = 0; fseek(fs, 0, SEEK_END); FileTestLength = ftell(fs); fseek(fs, 0, SEEK_SET); pFileTestBuffer = (UByte*)OVR_ALLOC(FileTestLength); if (pFileTestBuffer) { OVR_ASSERT(FileTestLength == (unsigned)Read(pFileTestBuffer, FileTestLength)); Seek(0, Seek_Set); } #endif ErrorCode = 0; } LastOp = 0; } const char* FILEFile::GetFilePath() { return FileName.ToCStr(); } // ** File Information bool FILEFile::IsValid() { return Opened; } bool FILEFile::IsWritable() { return IsValid() && (OpenFlags&Open_Write); } /* bool FILEFile::IsRecoverable() { return IsValid() && ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC); } */ // Return position / file size int FILEFile::Tell() { int pos = (int)ftell (fs); if (pos < 0) ErrorCode = SFerror(); return pos; } SInt64 FILEFile::LTell() { SInt64 pos = ftell(fs); if (pos < 0) ErrorCode = SFerror(); return pos; } int FILEFile::GetLength() { int pos = Tell(); if (pos >= 0) { Seek (0, Seek_End); int size = Tell(); Seek (pos, Seek_Set); return size; } return -1; } SInt64 FILEFile::LGetLength() { SInt64 pos = LTell(); if (pos >= 0) { LSeek (0, Seek_End); SInt64 size = LTell(); LSeek (pos, Seek_Set); return size; } return -1; } int FILEFile::GetErrorCode() { return ErrorCode; } // ** Stream implementation & I/O int FILEFile::Write(const UByte *pbuffer, int numBytes) { if (LastOp && LastOp != Open_Write) fflush(fs); LastOp = Open_Write; int written = (int) fwrite(pbuffer, 1, numBytes, fs); if (written < numBytes) ErrorCode = SFerror(); #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (written > 0) TestPos += written; #endif return written; } int FILEFile::Read(UByte *pbuffer, int numBytes) { if (LastOp && LastOp != Open_Read) fflush(fs); LastOp = Open_Read; int read = (int) fread(pbuffer, 1, numBytes, fs); if (read < numBytes) ErrorCode = SFerror(); #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (read > 0) { // Read-in data must match our pre-loaded buffer data! UByte* pcompareBuffer = pFileTestBuffer + TestPos; for (int i=0; i< read; i++) { OVR_ASSERT(pcompareBuffer[i] == pbuffer[i]); } //OVR_ASSERT(!memcmp(pFileTestBuffer + TestPos, pbuffer, read)); TestPos += read; OVR_ASSERT(ftell(fs) == (int)TestPos); } #endif return read; } // Seeks ahead to skip bytes int FILEFile::SkipBytes(int numBytes) { SInt64 pos = LTell(); SInt64 newPos = LSeek(numBytes, Seek_Cur); // Return -1 for major error if ((pos==-1) || (newPos==-1)) { return -1; } //ErrorCode = ((NewPos-Pos) int(sizeof(buff))) ? int(sizeof(buff)) : byteSize; szRead = pstream->Read(buff, szRequest); szWritten = 0; if (szRead > 0) szWritten = Write(buff, szRead); count += szWritten; byteSize -= szWritten; if (szWritten < szRequest) break; } return count; } bool FILEFile::Close() { #ifdef OVR_FILE_VERIFY_SEEK_ERRORS if (pFileTestBuffer) { OVR_FREE(pFileTestBuffer); pFileTestBuffer = 0; FileTestLength = 0; } #endif bool closeRet = !fclose(fs); if (!closeRet) { ErrorCode = SFerror(); return 0; } else { Opened = 0; fs = 0; ErrorCode = 0; } // Handle safe truncate /* if ((OpenFlags & OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) { // Delete original file (if it existed) DWORD oldAttributes = FileUtilWin32::GetFileAttributes(FileName); if (oldAttributes!=0xFFFFFFFF) if (!FileUtilWin32::DeleteFile(FileName)) { // Try to remove the readonly attribute FileUtilWin32::SetFileAttributes(FileName, oldAttributes & (~FILE_ATTRIBUTE_READONLY) ); // And delete the file again if (!FileUtilWin32::DeleteFile(FileName)) return 0; } // Rename temp file to real filename if (!FileUtilWin32::MoveFile(TempName, FileName)) { //ErrorCode = errno; return 0; } } */ return 1; } /* bool FILEFile::CloseCancel() { bool closeRet = (bool)::CloseHandle(fd); if (!closeRet) { //ErrorCode = errno; return 0; } else { Opened = 0; fd = INVALID_HANDLE_VALUE; ErrorCode = 0; } // Handle safe truncate (delete tmp file, leave original unchanged) if ((OpenFlags&OVR_FO_SAFETRUNC) == OVR_FO_SAFETRUNC) if (!FileUtilWin32::DeleteFile(TempName)) { //ErrorCode = errno; return 0; } return 1; } */ Ptr FileFILEOpen(const String& path, int flags, int mode) { Ptr result = *new FILEFile(path, flags, mode); return result; } // Helper function: obtain file information time. bool SysFile::GetFileStat(FileStat* pfileStat, const String& path) { #if defined(OVR_OS_WIN32) // 64-bit implementation on Windows. struct __stat64 fileStat; // Stat returns 0 for success. wchar_t *pwpath = (wchar_t*)OVR_ALLOC((UTF8Util::GetLength(path.ToCStr())+1)*sizeof(wchar_t)); UTF8Util::DecodeString(pwpath, path.ToCStr()); int ret = _wstat64(pwpath, &fileStat); OVR_FREE(pwpath); if (ret) return false; #else struct stat fileStat; // Stat returns 0 for success. if (stat(path, &fileStat) != 0) return false; #endif pfileStat->AccessTime = fileStat.st_atime; pfileStat->ModifyTime = fileStat.st_mtime; pfileStat->FileSize = fileStat.st_size; return true; } } // Namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Hash.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Hash.h new file mode 100644 index 0000000..47a7edb --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Hash.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_Hash.h Content : Template hash-table/set implementation Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Hash_h #define OVR_Hash_h #include "OVR_ContainerAllocator.h" #include "OVR_Alg.h" // 'new' operator is redefined/used in this file. #undef new namespace OVR { //----------------------------------------------------------------------------------- // ***** Hash Table Implementation // HastSet and Hash. // // Hash table, linear probing, internal chaining. One interesting/nice thing // about this implementation is that the table itself is a flat chunk of memory // containing no pointers, only relative indices. If the key and value types // of the Hash contain no pointers, then the Hash can be serialized using raw IO. // // Never shrinks, unless you explicitly Clear() it. Expands on // demand, though. For best results, if you know roughly how big your // table will be, default it to that size when you create it. // // Key usability feature: // // 1. Allows node hash values to either be cached or not. // // 2. Allows for alternative keys with methods such as GetAlt(). Handy // if you need to search nodes by their components; no need to create // temporary nodes. // // *** Hash functors: // // IdentityHash - use when the key is already a good hash // HFixedSizeHash - general hash based on object's in-memory representation. // Hash is just the input value; can use this for integer-indexed hash tables. template class IdentityHash { public: UPInt operator()(const C& data) const { return (UPInt) data; } }; // Computes a hash of an object's representation. template class FixedSizeHash { public: // Alternative: "sdbm" hash function, suggested at same web page // above, http::/www.cs.yorku.ca/~oz/hash.html // This is somewhat slower then Bernstein, but it works way better than the above // hash function for hashing large numbers of 32-bit ints. static OVR_FORCE_INLINE UPInt SDBM_Hash(const void* data_in, UPInt size, UPInt seed = 5381) { const UByte* data = (const UByte*) data_in; UPInt h = seed; while (size > 0) { size--; h = (h << 16) + (h << 6) - h + (UPInt)data[size]; } return h; } UPInt operator()(const C& data) const { unsigned char* p = (unsigned char*) &data; int size = sizeof(C); return SDBM_Hash(p, size); } }; // *** HashsetEntry Entry types. // Compact hash table Entry type that re-computes hash keys during hash traversal. // Good to use if the hash function is cheap or the hash value is already cached in C. template class HashsetEntry { public: // Internal chaining for collisions. SPInt NextInChain; C Value; HashsetEntry() : NextInChain(-2) { } HashsetEntry(const HashsetEntry& e) : NextInChain(e.NextInChain), Value(e.Value) { } HashsetEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } // Cached hash value access - can be optimized bu storing hash locally. // Mask value only needs to be used if SetCachedHash is not implemented. UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } void SetCachedHash(UPInt) {} void Clear() { Value.~C(); // placement delete NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; // Hash table Entry type that caches the Entry hash value for nodes, so that it // does not need to be re-computed during access. template class HashsetCachedEntry { public: // Internal chaining for collisions. SPInt NextInChain; UPInt HashValue; C Value; HashsetCachedEntry() : NextInChain(-2) { } HashsetCachedEntry(const HashsetCachedEntry& e) : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } HashsetCachedEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } // Cached hash value access - can be optimized bu storing hash locally. // Mask value only needs to be used if SetCachedHash is not implemented. UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } void Clear() { Value.~C(); NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; //----------------------------------------------------------------------------------- // *** HashSet implementation - relies on either cached or regular entries. // // Use: Entry = HashsetCachedEntry if hashes are expensive to // compute and thus need caching in entries. // Entry = HashsetEntry if hashes are already externally cached. // template, class AltHashF = HashF, class Allocator = ContainerAllocator, class Entry = HashsetCachedEntry > class HashSetBase { enum { HashMinSize = 8 }; public: OVR_MEMORY_REDEFINE_NEW(HashSetBase) typedef HashSetBase SelfType; HashSetBase() : pTable(NULL) { } HashSetBase(int sizeHint) : pTable(NULL) { SetCapacity(this, sizeHint); } HashSetBase(const SelfType& src) : pTable(NULL) { Assign(this, src); } ~HashSetBase() { if (pTable) { // Delete the entries. for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (!e->IsEmpty()) e->Free(); } Allocator::Free(pTable); pTable = NULL; } } void Assign(const SelfType& src) { Clear(); if (src.IsEmpty() == false) { SetCapacity(src.GetSize()); for (ConstIterator it = src.Begin(); it != src.End(); ++it) { Add(*it); } } } // Remove all entries from the HashSet table. void Clear() { if (pTable) { // Delete the entries. for (UPInt i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (!e->IsEmpty()) e->Clear(); } Allocator::Free(pTable); pTable = NULL; } } // Returns true if the HashSet is empty. bool IsEmpty() const { return pTable == NULL || pTable->EntryCount == 0; } // Set a new or existing value under the key, to the value. // Pass a different class of 'key' so that assignment reference object // can be passed instead of the actual object. template void Set(const CRef& key) { UPInt hashValue = HashF()(key); SPInt index = (SPInt)-1; if (pTable != NULL) index = findIndexCore(key, hashValue & pTable->SizeMask); if (index >= 0) { E(index).Value = key; } else { // Entry under key doesn't exist. add(key, hashValue); } } template inline void Add(const CRef& key) { UPInt hashValue = HashF()(key); add(key, hashValue); } // Remove by alternative key. template void RemoveAlt(const K& key) { if (pTable == NULL) return; UPInt hashValue = AltHashF()(key); SPInt index = hashValue & pTable->SizeMask; Entry* e = &E(index); // If empty node or occupied by collider, we have nothing to remove. if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != (UPInt)index)) return; // Save index SPInt naturalIndex = index; SPInt prevIndex = -1; while ((e->GetCachedHash(pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) { // Keep looking through the chain. prevIndex = index; index = e->NextInChain; if (index == -1) return; // End of chain, item not found e = &E(index); } // Found it - our item is at index if (naturalIndex == index) { // If we have a follower, move it to us if (!e->IsEndOfChain()) { Entry* enext = &E(e->NextInChain); e->Clear(); new (e) Entry(*enext); // Point us to the follower's cell that will be cleared e = enext; } } else { // We are not at natural index, so deal with the prev items next index E(prevIndex).NextInChain = e->NextInChain; } // Clear us, of the follower cell that was moved. e->Clear(); pTable->EntryCount --; // Should we check the size to condense hash? ... } // Remove by main key. template void Remove(const CRef& key) { RemoveAlt(key); } // Retrieve the pointer to a value under the given key. // - If there's no value under the key, then return NULL. // - If there is a value, return the pointer. template C* Get(const K& key) { SPInt index = findIndex(key); if (index >= 0) return &E(index).Value; return 0; } template const C* Get(const K& key) const { SPInt index = findIndex(key); if (index >= 0) return &E(index).Value; return 0; } // Alternative key versions of Get. Used by Hash. template const C* GetAlt(const K& key) const { SPInt index = findIndexAlt(key); if (index >= 0) return &E(index).Value; return 0; } template C* GetAlt(const K& key) { SPInt index = findIndexAlt(key); if (index >= 0) return &E(index).Value; return 0; } template bool GetAlt(const K& key, C* pval) const { SPInt index = findIndexAlt(key); if (index >= 0) { if (pval) *pval = E(index).Value; return true; } return false; } UPInt GetSize() const { return pTable == NULL ? 0 : (UPInt)pTable->EntryCount; } // Resize the HashSet table to fit one more Entry. Often this // doesn't involve any action. void CheckExpand() { if (pTable == NULL) { // Initial creation of table. Make a minimum-sized table. setRawCapacity(HashMinSize); } else if (pTable->EntryCount * 5 > (pTable->SizeMask + 1) * 4) { // pTable is more than 5/4 ths full. Expand. setRawCapacity((pTable->SizeMask + 1) * 2); } } // Hint the bucket count to >= n. void Resize(UPInt n) { // Not really sure what this means in relation to // STLport's hash_map... they say they "increase the // bucket count to at least n" -- but does that mean // their real capacity after Resize(n) is more like // n*2 (since they do linked-list chaining within // buckets?). SetCapacity(n); } // Size the HashSet so that it can comfortably contain the given // number of elements. If the HashSet already contains more // elements than newSize, then this may be a no-op. void SetCapacity(UPInt newSize) { UPInt newRawSize = (newSize * 5) / 4; if (newRawSize <= GetSize()) return; setRawCapacity(newRawSize); } // Disable inappropriate 'operator ->' warning on MSVC6. #ifdef OVR_CC_MSVC #if (OVR_CC_MSVC < 1300) # pragma warning(disable : 4284) #endif #endif // Iterator API, like STL. struct ConstIterator { const C& operator * () const { OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); return pHash->E(Index).Value; } const C* operator -> () const { OVR_ASSERT(Index >= 0 && Index <= (SPInt)pHash->pTable->SizeMask); return &pHash->E(Index).Value; } void operator ++ () { // Find next non-empty Entry. if (Index <= (SPInt)pHash->pTable->SizeMask) { Index++; while ((UPInt)Index <= pHash->pTable->SizeMask && pHash->E(Index).IsEmpty()) { Index++; } } } bool operator == (const ConstIterator& it) const { if (IsEnd() && it.IsEnd()) { return true; } else { return (pHash == it.pHash) && (Index == it.Index); } } bool operator != (const ConstIterator& it) const { return ! (*this == it); } bool IsEnd() const { return (pHash == NULL) || (pHash->pTable == NULL) || (Index > (SPInt)pHash->pTable->SizeMask); } ConstIterator() : pHash(NULL), Index(0) { } public: // Constructor was intentionally made public to allow create // iterator with arbitrary index. ConstIterator(const SelfType* h, SPInt index) : pHash(h), Index(index) { } const SelfType* GetContainer() const { return pHash; } SPInt GetIndex() const { return Index; } protected: friend class HashSetBase; const SelfType* pHash; SPInt Index; }; friend struct ConstIterator; // Non-const Iterator; Get most of it from ConstIterator. struct Iterator : public ConstIterator { // Allow non-const access to entries. C& operator*() const { OVR_ASSERT(ConstIterator::Index >= 0 && ConstIterator::Index <= (SPInt)ConstIterator::pHash->pTable->SizeMask); return const_cast(ConstIterator::pHash)->E(ConstIterator::Index).Value; } C* operator->() const { return &(operator*()); } Iterator() : ConstIterator(NULL, 0) { } // Removes current element from Hash void Remove() { RemoveAlt(operator*()); } template void RemoveAlt(const K& key) { SelfType* phash = const_cast(ConstIterator::pHash); //Entry* ee = &phash->E(ConstIterator::Index); //const C& key = ee->Value; UPInt hashValue = AltHashF()(key); SPInt index = hashValue & phash->pTable->SizeMask; Entry* e = &phash->E(index); // If empty node or occupied by collider, we have nothing to remove. if (e->IsEmpty() || (e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)index)) return; // Save index SPInt naturalIndex = index; SPInt prevIndex = -1; while ((e->GetCachedHash(phash->pTable->SizeMask) != (UPInt)naturalIndex) || !(e->Value == key)) { // Keep looking through the chain. prevIndex = index; index = e->NextInChain; if (index == -1) return; // End of chain, item not found e = &phash->E(index); } if (index == (SPInt)ConstIterator::Index) { // Found it - our item is at index if (naturalIndex == index) { // If we have a follower, move it to us if (!e->IsEndOfChain()) { Entry* enext = &phash->E(e->NextInChain); e->Clear(); new (e) Entry(*enext); // Point us to the follower's cell that will be cleared e = enext; --ConstIterator::Index; } } else { // We are not at natural index, so deal with the prev items next index phash->E(prevIndex).NextInChain = e->NextInChain; } // Clear us, of the follower cell that was moved. e->Clear(); phash->pTable->EntryCount --; } else OVR_ASSERT(0); //? } private: friend class HashSetBase; Iterator(SelfType* h, SPInt i0) : ConstIterator(h, i0) { } }; friend struct Iterator; Iterator Begin() { if (pTable == 0) return Iterator(NULL, 0); // Scan till we hit the First valid Entry. UPInt i0 = 0; while (i0 <= pTable->SizeMask && E(i0).IsEmpty()) { i0++; } return Iterator(this, i0); } Iterator End() { return Iterator(NULL, 0); } ConstIterator Begin() const { return const_cast(this)->Begin(); } ConstIterator End() const { return const_cast(this)->End(); } template Iterator Find(const K& key) { SPInt index = findIndex(key); if (index >= 0) return Iterator(this, index); return Iterator(NULL, 0); } template Iterator FindAlt(const K& key) { SPInt index = findIndexAlt(key); if (index >= 0) return Iterator(this, index); return Iterator(NULL, 0); } template ConstIterator Find(const K& key) const { return const_cast(this)->Find(key); } template ConstIterator FindAlt(const K& key) const { return const_cast(this)->FindAlt(key); } private: // Find the index of the matching Entry. If no match, then return -1. template SPInt findIndex(const K& key) const { if (pTable == NULL) return -1; UPInt hashValue = HashF()(key) & pTable->SizeMask; return findIndexCore(key, hashValue); } template SPInt findIndexAlt(const K& key) const { if (pTable == NULL) return -1; UPInt hashValue = AltHashF()(key) & pTable->SizeMask; return findIndexCore(key, hashValue); } // Find the index of the matching Entry. If no match, then return -1. template SPInt findIndexCore(const K& key, UPInt hashValue) const { // Table must exist. OVR_ASSERT(pTable != 0); // Hash key must be 'and-ed' by the caller. OVR_ASSERT((hashValue & ~pTable->SizeMask) == 0); UPInt index = hashValue; const Entry* e = &E(index); // If empty or occupied by a collider, not found. if (e->IsEmpty() || (e->GetCachedHash(pTable->SizeMask) != index)) return -1; while(1) { OVR_ASSERT(e->GetCachedHash(pTable->SizeMask) == hashValue); if (e->GetCachedHash(pTable->SizeMask) == hashValue && e->Value == key) { // Found it. return index; } // Values can not be equal at this point. // That would mean that the hash key for the same value differs. OVR_ASSERT(!(e->Value == key)); // Keep looking through the chain. index = e->NextInChain; if (index == (UPInt)-1) break; // end of chain e = &E(index); OVR_ASSERT(!e->IsEmpty()); } return -1; } // Add a new value to the HashSet table, under the specified key. template void add(const CRef& key, UPInt hashValue) { CheckExpand(); hashValue &= pTable->SizeMask; pTable->EntryCount++; SPInt index = hashValue; Entry* naturalEntry = &(E(index)); if (naturalEntry->IsEmpty()) { // Put the new Entry in. new (naturalEntry) Entry(key, -1); } else { // Find a blank spot. SPInt blankIndex = index; do { blankIndex = (blankIndex + 1) & pTable->SizeMask; } while(!E(blankIndex).IsEmpty()); Entry* blankEntry = &E(blankIndex); if (naturalEntry->GetCachedHash(pTable->SizeMask) == (UPInt)index) { // Collision. Link into this chain. // Move existing list head. new (blankEntry) Entry(*naturalEntry); // placement new, copy ctor // Put the new info in the natural Entry. naturalEntry->Value = key; naturalEntry->NextInChain = blankIndex; } else { // Existing Entry does not naturally // belong in this slot. Existing // Entry must be moved. // Find natural location of collided element (i.e. root of chain) SPInt collidedIndex = naturalEntry->GetCachedHash(pTable->SizeMask); OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); for (;;) { Entry* e = &E(collidedIndex); if (e->NextInChain == index) { // Here's where we need to splice. new (blankEntry) Entry(*naturalEntry); e->NextInChain = blankIndex; break; } collidedIndex = e->NextInChain; OVR_ASSERT(collidedIndex >= 0 && collidedIndex <= (SPInt)pTable->SizeMask); } // Put the new data in the natural Entry. naturalEntry->Value = key; naturalEntry->NextInChain = -1; } } // Record hash value: has effect only if cached node is used. naturalEntry->SetCachedHash(hashValue); } // Index access helpers. Entry& E(UPInt index) { // Must have pTable and access needs to be within bounds. OVR_ASSERT(index <= pTable->SizeMask); return *(((Entry*) (pTable + 1)) + index); } const Entry& E(UPInt index) const { OVR_ASSERT(index <= pTable->SizeMask); return *(((Entry*) (pTable + 1)) + index); } // Resize the HashSet table to the given size (Rehash the // contents of the current table). The arg is the number of // HashSet table entries, not the number of elements we should // actually contain (which will be less than this). void setRawCapacity(UPInt newSize) { if (newSize == 0) { // Special case. Clear(); return; } // Minimum size; don't incur rehashing cost when expanding // very small tables. Not that we perform this check before // 'log2f' call to avoid fp exception with newSize == 1. if (newSize < HashMinSize) newSize = HashMinSize; else { // Force newSize to be a power of two. int bits = Alg::UpperBit(newSize-1) + 1; // Chop( Log2f((float)(newSize-1)) + 1); OVR_ASSERT((UPInt(1) << bits) >= newSize); newSize = UPInt(1) << bits; } SelfType newHash; newHash.pTable = (TableType*) Allocator::Alloc( sizeof(TableType) + sizeof(Entry) * newSize); // Need to do something on alloc failure! OVR_ASSERT(newHash.pTable); newHash.pTable->EntryCount = 0; newHash.pTable->SizeMask = newSize - 1; UPInt i, n; // Mark all entries as empty. for (i = 0; i < newSize; i++) newHash.E(i).NextInChain = -2; // Copy stuff to newHash if (pTable) { for (i = 0, n = pTable->SizeMask; i <= n; i++) { Entry* e = &E(i); if (e->IsEmpty() == false) { // Insert old Entry into new HashSet. newHash.Add(e->Value); // placement delete of old element e->Clear(); } } // Delete our old data buffer. Allocator::Free(pTable); } // Steal newHash's data. pTable = newHash.pTable; newHash.pTable = NULL; } struct TableType { UPInt EntryCount; UPInt SizeMask; // Entry array follows this structure // in memory. }; TableType* pTable; }; //----------------------------------------------------------------------------------- template, class AltHashF = HashF, class Allocator = ContainerAllocator, class Entry = HashsetCachedEntry > class HashSet : public HashSetBase { public: typedef HashSetBase BaseType; typedef HashSet SelfType; HashSet() { } HashSet(int sizeHint) : BaseType(sizeHint) { } HashSet(const SelfType& src) : BaseType(src) { } ~HashSet() { } void operator = (const SelfType& src) { BaseType::Assign(src); } // Set a new or existing value under the key, to the value. // Pass a different class of 'key' so that assignment reference object // can be passed instead of the actual object. template void Set(const CRef& key) { BaseType::Set(key); } template inline void Add(const CRef& key) { BaseType::Add(key); } // Hint the bucket count to >= n. void Resize(UPInt n) { BaseType::SetCapacity(n); } // Size the HashSet so that it can comfortably contain the given // number of elements. If the HashSet already contains more // elements than newSize, then this may be a no-op. void SetCapacity(UPInt newSize) { BaseType::SetCapacity(newSize); } }; // HashSet with uncached hash code; declared for convenience. template, class AltHashF = HashF, class Allocator = ContainerAllocator > class HashSetUncached : public HashSet > { public: typedef HashSetUncached SelfType; typedef HashSet > BaseType; // Delegated constructors. HashSetUncached() { } HashSetUncached(int sizeHint) : BaseType(sizeHint) { } HashSetUncached(const SelfType& src) : BaseType(src) { } ~HashSetUncached() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; //----------------------------------------------------------------------------------- // ***** Hash hash table implementation // Node for Hash - necessary so that Hash can delegate its implementation // to HashSet. template struct HashNode { typedef HashNode SelfType; typedef C FirstType; typedef U SecondType; C First; U Second; // NodeRef is used to allow passing of elements into HashSet // without using a temporary object. struct NodeRef { const C* pFirst; const U* pSecond; NodeRef(const C& f, const U& s) : pFirst(&f), pSecond(&s) { } NodeRef(const NodeRef& src) : pFirst(src.pFirst), pSecond(src.pSecond) { } // Enable computation of ghash_node_hashf. inline UPInt GetHash() const { return HashF()(*pFirst); } // Necessary conversion to allow HashNode::operator == to work. operator const C& () const { return *pFirst; } }; // Note: No default constructor is necessary. HashNode(const HashNode& src) : First(src.First), Second(src.Second) { } HashNode(const NodeRef& src) : First(*src.pFirst), Second(*src.pSecond) { } void operator = (const NodeRef& src) { First = *src.pFirst; Second = *src.pSecond; } template bool operator == (const K& src) const { return (First == src); } template static UPInt CalcHash(const K& data) { return HashF()(data); } inline UPInt GetHash() const { return HashF()(First); } // Hash functors used with this node. A separate functor is used for alternative // key lookup so that it does not need to access the '.First' element. struct NodeHashF { template UPInt operator()(const K& data) const { return data.GetHash(); } }; struct NodeAltHashF { template UPInt operator()(const K& data) const { return HashNode::CalcHash(data); } }; }; // **** Extra hashset_entry types to allow NodeRef construction. // The big difference between the below types and the ones used in hash_set is that // these allow initializing the node with 'typename C::NodeRef& keyRef', which // is critical to avoid temporary node allocation on stack when using placement new. // Compact hash table Entry type that re-computes hash keys during hash traversal. // Good to use if the hash function is cheap or the hash value is already cached in C. template class HashsetNodeEntry { public: // Internal chaining for collisions. SPInt NextInChain; C Value; HashsetNodeEntry() : NextInChain(-2) { } HashsetNodeEntry(const HashsetNodeEntry& e) : NextInChain(e.NextInChain), Value(e.Value) { } HashsetNodeEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } HashsetNodeEntry(const typename C::NodeRef& keyRef, SPInt next) : NextInChain(next), Value(keyRef) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } UPInt GetCachedHash(UPInt maskValue) const { return HashF()(Value) & maskValue; } void SetCachedHash(UPInt hashValue) { OVR_UNUSED(hashValue); } void Clear() { Value.~C(); // placement delete NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; // Hash table Entry type that caches the Entry hash value for nodes, so that it // does not need to be re-computed during access. template class HashsetCachedNodeEntry { public: // Internal chaining for collisions. SPInt NextInChain; UPInt HashValue; C Value; HashsetCachedNodeEntry() : NextInChain(-2) { } HashsetCachedNodeEntry(const HashsetCachedNodeEntry& e) : NextInChain(e.NextInChain), HashValue(e.HashValue), Value(e.Value) { } HashsetCachedNodeEntry(const C& key, SPInt next) : NextInChain(next), Value(key) { } HashsetCachedNodeEntry(const typename C::NodeRef& keyRef, SPInt next) : NextInChain(next), Value(keyRef) { } bool IsEmpty() const { return NextInChain == -2; } bool IsEndOfChain() const { return NextInChain == -1; } UPInt GetCachedHash(UPInt maskValue) const { OVR_UNUSED(maskValue); return HashValue; } void SetCachedHash(UPInt hashValue) { HashValue = hashValue; } void Clear() { Value.~C(); NextInChain = -2; } // Free is only used from dtor of hash; Clear is used during regular operations: // assignment, hash reallocations, value reassignments, so on. void Free() { Clear(); } }; //----------------------------------------------------------------------------------- template, class Allocator = ContainerAllocator, class HashNode = OVR::HashNode, class Entry = HashsetCachedNodeEntry, class Container = HashSet > class Hash { public: OVR_MEMORY_REDEFINE_NEW(Hash) // Types used for hash_set. typedef U ValueType; typedef Hash SelfType; // Actual hash table itself, implemented as hash_set. Container mHash; public: Hash() { } Hash(int sizeHint) : mHash(sizeHint) { } Hash(const SelfType& src) : mHash(src.mHash) { } ~Hash() { } void operator = (const SelfType& src) { mHash = src.mHash; } // Remove all entries from the Hash table. inline void Clear() { mHash.Clear(); } // Returns true if the Hash is empty. inline bool IsEmpty() const { return mHash.IsEmpty(); } // Access (set). inline void Set(const C& key, const U& value) { typename HashNode::NodeRef e(key, value); mHash.Set(e); } inline void Add(const C& key, const U& value) { typename HashNode::NodeRef e(key, value); mHash.Add(e); } // Removes an element by clearing its Entry. inline void Remove(const C& key) { mHash.RemoveAlt(key); } template inline void RemoveAlt(const K& key) { mHash.RemoveAlt(key); } // Retrieve the value under the given key. // - If there's no value under the key, then return false and leave *pvalue alone. // - If there is a value, return true, and Set *Pvalue to the Entry's value. // - If value == NULL, return true or false according to the presence of the key. bool Get(const C& key, U* pvalue) const { const HashNode* p = mHash.GetAlt(key); if (p) { if (pvalue) *pvalue = p->Second; return true; } return false; } template bool GetAlt(const K& key, U* pvalue) const { const HashNode* p = mHash.GetAlt(key); if (p) { if (pvalue) *pvalue = p->Second; return true; } return false; } // Retrieve the pointer to a value under the given key. // - If there's no value under the key, then return NULL. // - If there is a value, return the pointer. inline U* Get(const C& key) { HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } inline const U* Get(const C& key) const { const HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } template inline U* GetAlt(const K& key) { HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } template inline const U* GetAlt(const K& key) const { const HashNode* p = mHash.GetAlt(key); return p ? &p->Second : 0; } // Sizing methods - delegate to Hash. inline UPInt GetSize() const { return mHash.GetSize(); } inline void Resize(UPInt n) { mHash.Resize(n); } inline void SetCapacity(UPInt newSize) { mHash.SetCapacity(newSize); } // Iterator API, like STL. typedef typename Container::ConstIterator ConstIterator; typedef typename Container::Iterator Iterator; inline Iterator Begin() { return mHash.Begin(); } inline Iterator End() { return mHash.End(); } inline ConstIterator Begin() const { return mHash.Begin(); } inline ConstIterator End() const { return mHash.End(); } Iterator Find(const C& key) { return mHash.FindAlt(key); } ConstIterator Find(const C& key) const { return mHash.FindAlt(key); } template Iterator FindAlt(const K& key) { return mHash.FindAlt(key); } template ConstIterator FindAlt(const K& key) const { return mHash.FindAlt(key); } }; // Hash with uncached hash code; declared for convenience. template, class Allocator = ContainerAllocator > class HashUncached : public Hash, HashsetNodeEntry, typename HashNode::NodeHashF> > { public: typedef HashUncached SelfType; typedef Hash, HashsetNodeEntry, typename HashNode::NodeHashF> > BaseType; // Delegated constructors. HashUncached() { } HashUncached(int sizeHint) : BaseType(sizeHint) { } HashUncached(const SelfType& src) : BaseType(src) { } ~HashUncached() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; // And identity hash in which keys serve as hash value. Can be uncached, // since hash computation is assumed cheap. template, class HashF = IdentityHash > class HashIdentity : public HashUncached { public: typedef HashIdentity SelfType; typedef HashUncached BaseType; // Delegated constructors. HashIdentity() { } HashIdentity(int sizeHint) : BaseType(sizeHint) { } HashIdentity(const SelfType& src) : BaseType(src) { } ~HashIdentity() { } void operator = (const SelfType& src) { BaseType::operator = (src); } }; } // OVR #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_KeyCodes.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_KeyCodes.h new file mode 100644 index 0000000..c83f9bf --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_KeyCodes.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_KeyCodes.h Content : Common keyboard constants Created : September 19, 2012 Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_KeyCodes_h #define OVR_KeyCodes_h namespace OVR { //----------------------------------------------------------------------------------- // ***** KeyCode // KeyCode enumeration defines platform-independent keyboard key constants. // Note that Key_A through Key_Z are mapped to capital ascii constants. enum KeyCode { // Key_None indicates that no key was specified. Key_None = 0, // A through Z and numbers 0 through 9. Key_A = 65, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G, Key_H, Key_I, Key_J, Key_K, Key_L, Key_M, Key_N, Key_O, Key_P, Key_Q, Key_R, Key_S, Key_T, Key_U, Key_V, Key_W, Key_X, Key_Y, Key_Z, Key_Num0 = 48, Key_Num1, Key_Num2, Key_Num3, Key_Num4, Key_Num5, Key_Num6, Key_Num7, Key_Num8, Key_Num9, // Numeric keypad. Key_KP_0 = 0xa0, Key_KP_1, Key_KP_2, Key_KP_3, Key_KP_4, Key_KP_5, Key_KP_6, Key_KP_7, Key_KP_8, Key_KP_9, Key_KP_Multiply, Key_KP_Add, Key_KP_Enter, Key_KP_Subtract, Key_KP_Decimal, Key_KP_Divide, // Function keys. Key_F1 = 0xb0, Key_F2, Key_F3, Key_F4, Key_F5, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11, Key_F12, Key_F13, Key_F14, Key_F15, // Other keys. Key_Backspace = 8, Key_Tab, Key_Clear = 12, Key_Return, Key_Shift = 16, Key_Control, Key_Alt, Key_Pause, Key_CapsLock = 20, // Toggle Key_Escape = 27, Key_Space = 32, Key_Quote = 39, Key_PageUp = 0xc0, Key_PageDown, Key_End, Key_Home, Key_Left, Key_Up, Key_Right, Key_Down, Key_Insert, Key_Delete, Key_Help, Key_Comma = 44, Key_Minus, Key_Slash = 47, Key_Period, Key_NumLock = 144, // Toggle Key_ScrollLock = 145, // Toggle Key_Semicolon = 59, Key_Equal = 61, Key_Backtick = 96, // ` and tilda~ when shifted (US keyboard) Key_BracketLeft = 91, Key_Backslash, Key_BracketRight, Key_OEM_AX = 0xE1, // 'AX' key on Japanese AX keyboard Key_OEM_102 = 0xE2, // "<>" or "\|" on RT 102-key keyboard. Key_ICO_HELP = 0xE3, // Help key on ICO Key_ICO_00 = 0xE4, // 00 key on ICO Key_Meta, // Total number of keys. Key_CodeCount }; //----------------------------------------------------------------------------------- class KeyModifiers { public: enum { Key_ShiftPressed = 0x01, Key_CtrlPressed = 0x02, Key_AltPressed = 0x04, Key_MetaPressed = 0x08, Key_CapsToggled = 0x10, Key_NumToggled = 0x20, Key_ScrollToggled = 0x40, Initialized_Bit = 0x80, Initialized_Mask = 0xFF }; unsigned char States; KeyModifiers() : States(0) { } KeyModifiers(unsigned char st) : States((unsigned char)(st | Initialized_Bit)) { } void Reset() { States = 0; } bool IsShiftPressed() const { return (States & Key_ShiftPressed) != 0; } bool IsCtrlPressed() const { return (States & Key_CtrlPressed) != 0; } bool IsAltPressed() const { return (States & Key_AltPressed) != 0; } bool IsMetaPressed() const { return (States & Key_MetaPressed) != 0; } bool IsCapsToggled() const { return (States & Key_CapsToggled) != 0; } bool IsNumToggled() const { return (States & Key_NumToggled) != 0; } bool IsScrollToggled() const{ return (States & Key_ScrollToggled) != 0; } void SetShiftPressed(bool v = true) { (v) ? States |= Key_ShiftPressed : States &= ~Key_ShiftPressed; } void SetCtrlPressed(bool v = true) { (v) ? States |= Key_CtrlPressed : States &= ~Key_CtrlPressed; } void SetAltPressed(bool v = true) { (v) ? States |= Key_AltPressed : States &= ~Key_AltPressed; } void SetMetaPressed(bool v = true) { (v) ? States |= Key_MetaPressed : States &= ~Key_MetaPressed; } void SetCapsToggled(bool v = true) { (v) ? States |= Key_CapsToggled : States &= ~Key_CapsToggled; } void SetNumToggled(bool v = true) { (v) ? States |= Key_NumToggled : States &= ~Key_NumToggled; } void SetScrollToggled(bool v = true) { (v) ? States |= Key_ScrollToggled: States &= ~Key_ScrollToggled; } bool IsInitialized() const { return (States & Initialized_Mask) != 0; } }; //----------------------------------------------------------------------------------- /* enum PadKeyCode { Pad_None, // Indicates absence of key code. Pad_Back, Pad_Start, Pad_A, Pad_B, Pad_X, Pad_Y, Pad_R1, // RightShoulder; Pad_L1, // LeftShoulder; Pad_R2, // RightTrigger; Pad_L2, // LeftTrigger; Pad_Up, Pad_Down, Pad_Right, Pad_Left, Pad_Plus, Pad_Minus, Pad_1, Pad_2, Pad_H, Pad_C, Pad_Z, Pad_O, Pad_T, Pad_S, Pad_Select, Pad_Home, Pad_RT, // RightThumb; Pad_LT // LeftThumb; }; */ } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_List.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_List.h new file mode 100644 index 0000000..2b8a13d --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_List.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_List.h Content : Template implementation for doubly-connected linked List Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_List_h #define OVR_List_h #include "OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** ListNode // // Base class for the elements of the intrusive linked list. // To store elements in the List do: // // struct MyData : ListNode // { // . . . // }; template struct ListNode { union { T* pPrev; void* pVoidPrev; }; union { T* pNext; void* pVoidNext; }; void RemoveNode() { pPrev->pNext = pNext; pNext->pPrev = pPrev; } // Removes us from the list and inserts pnew there instead. void ReplaceNodeWith(T* pnew) { pPrev->pNext = pnew; pNext->pPrev = pnew; pnew->pPrev = pPrev; pnew->pNext = pNext; } // Inserts the argument linked list node after us in the list. void InsertNodeAfter(T* p) { p->pPrev = pNext->pPrev; // this p->pNext = pNext; pNext->pPrev = p; pNext = p; } // Inserts the argument linked list node before us in the list. void InsertNodeBefore(T* p) { p->pNext = pNext->pPrev; // this p->pPrev = pPrev; pPrev->pNext = p; pPrev = p; } void Alloc_MoveTo(ListNode* pdest) { pdest->pNext = pNext; pdest->pPrev = pPrev; pPrev->pNext = (T*)pdest; pNext->pPrev = (T*)pdest; } }; //------------------------------------------------------------------------ // ***** List // // Doubly linked intrusive list. // The data type must be derived from ListNode. // // Adding: PushFront(), PushBack(). // Removing: Remove() - the element must be in the list! // Moving: BringToFront(), SendToBack() - the element must be in the list! // // Iterating: // MyData* data = MyList.GetFirst(); // while (!MyList.IsNull(data)) // { // . . . // data = MyList.GetNext(data); // } // // Removing: // MyData* data = MyList.GetFirst(); // while (!MyList.IsNull(data)) // { // MyData* next = MyList.GetNext(data); // if (ToBeRemoved(data)) // MyList.Remove(data); // data = next; // } // // List<> represents a doubly-linked list of T, where each T must derive // from ListNode. B specifies the base class that was directly // derived from ListNode, and is only necessary if there is an intermediate // inheritance chain. template class List { public: typedef T ValueType; List() { Root.pNext = Root.pPrev = (ValueType*)&Root; } void Clear() { Root.pNext = Root.pPrev = (ValueType*)&Root; } const ValueType* GetFirst() const { return (const ValueType*)Root.pNext; } const ValueType* GetLast () const { return (const ValueType*)Root.pPrev; } ValueType* GetFirst() { return (ValueType*)Root.pNext; } ValueType* GetLast () { return (ValueType*)Root.pPrev; } // Determine if list is empty (i.e.) points to itself. // Go through void* access to avoid issues with strict-aliasing optimizing out the // access after RemoveNode(), etc. bool IsEmpty() const { return Root.pVoidNext == (const T*)(const B*)&Root; } bool IsFirst(const ValueType* p) const { return p == Root.pNext; } bool IsLast (const ValueType* p) const { return p == Root.pPrev; } bool IsNull (const ValueType* p) const { return p == (const T*)(const B*)&Root; } inline static const ValueType* GetPrev(const ValueType* p) { return (const ValueType*)p->pPrev; } inline static const ValueType* GetNext(const ValueType* p) { return (const ValueType*)p->pNext; } inline static ValueType* GetPrev( ValueType* p) { return (ValueType*)p->pPrev; } inline static ValueType* GetNext( ValueType* p) { return (ValueType*)p->pNext; } void PushFront(ValueType* p) { p->pNext = Root.pNext; p->pPrev = (ValueType*)&Root; Root.pNext->pPrev = p; Root.pNext = p; } void PushBack(ValueType* p) { p->pPrev = Root.pPrev; p->pNext = (ValueType*)&Root; Root.pPrev->pNext = p; Root.pPrev = p; } static void Remove(ValueType* p) { p->pPrev->pNext = p->pNext; p->pNext->pPrev = p->pPrev; } void BringToFront(ValueType* p) { Remove(p); PushFront(p); } void SendToBack(ValueType* p) { Remove(p); PushBack(p); } // Appends the contents of the argument list to the front of this list; // items are removed from the argument list. void PushListToFront(List& src) { if (!src.IsEmpty()) { ValueType* pfirst = src.GetFirst(); ValueType* plast = src.GetLast(); src.Clear(); plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } void PushListToBack(List& src) { if (!src.IsEmpty()) { ValueType* pfirst = src.GetFirst(); ValueType* plast = src.GetLast(); src.Clear(); plast->pNext = (ValueType*)&Root; pfirst->pPrev = Root.pPrev; Root.pPrev->pNext = pfirst; Root.pPrev = plast; } } // Removes all source list items after (and including) the 'pfirst' node from the // source list and adds them to out list. void PushFollowingListItemsToFront(List& src, ValueType *pfirst) { if (pfirst != &src.Root) { ValueType *plast = src.Root.pPrev; // Remove list remainder from source. pfirst->pPrev->pNext = (ValueType*)&src.Root; src.Root.pPrev = pfirst->pPrev; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } // Removes all source list items up to but NOT including the 'pend' node from the // source list and adds them to out list. void PushPrecedingListItemsToFront(List& src, ValueType *ptail) { if (src.GetFirst() != ptail) { ValueType *pfirst = src.Root.pNext; ValueType *plast = ptail->pPrev; // Remove list remainder from source. ptail->pPrev = (ValueType*)&src.Root; src.Root.pNext = ptail; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } // Removes a range of source list items starting at 'pfirst' and up to, but not including 'pend', // and adds them to out list. Note that source items MUST already be in the list. void PushListItemsToFront(ValueType *pfirst, ValueType *pend) { if (pfirst != pend) { ValueType *plast = pend->pPrev; // Remove list remainder from source. pfirst->pPrev->pNext = pend; pend->pPrev = pfirst->pPrev; // Add the rest of the items to list. plast->pNext = Root.pNext; pfirst->pPrev = (ValueType*)&Root; Root.pNext->pPrev = plast; Root.pNext = pfirst; } } void Alloc_MoveTo(List* pdest) { if (IsEmpty()) pdest->Clear(); else { pdest->Root.pNext = Root.pNext; pdest->Root.pPrev = Root.pPrev; Root.pNext->pPrev = (ValueType*)&pdest->Root; Root.pPrev->pNext = (ValueType*)&pdest->Root; } } private: // Copying is prohibited List(const List&); const List& operator = (const List&); ListNode Root; }; //------------------------------------------------------------------------ // ***** FreeListElements // // Remove all elements in the list and free them in the allocator template void FreeListElements(List& list, Allocator& allocator) { typename List::ValueType* self = list.GetFirst(); while(!list.IsNull(self)) { typename List::ValueType* next = list.GetNext(self); allocator.Free(self); self = next; } list.Clear(); } } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Lockless.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Lockless.cpp new file mode 100644 index 0000000..6ceb6fe --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Lockless.cpp @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Lockless.cpp Content : Test logic for lock-less classes Created : December 27, 2013 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Lockless.h" #ifdef OVR_LOCKLESS_TEST #include "OVR_Threads.h" #include "OVR_Timer.h" #include "OVR_Log.h" namespace OVR { namespace LocklessTest { const int TestIterations = 10000000; // Use volatile dummys to force compiler to do spinning. volatile int Dummy1; int Unused1[32]; volatile int Dummy2; int Unused2[32]; volatile int Dummy3; int Unused3[32]; // Data block out of 20 consecutive integers, should be internally consistent. struct TestData { enum { ItemCount = 20 }; int Data[ItemCount]; void Set(int val) { for (int i=0; i TestDataUpdater; // Use this lock to verify that testing algorithm is otherwise correct... Lock TestLock; //------------------------------------------------------------------------------------- // Consumer thread reads values from TestDataUpdater and // ensures that each one is internally consistent. class Consumer : public Thread { virtual int Run() { LogText("LocklessTest::Consumer::Run started.\n"); while (!FirstItemWritten) { // spin until producer wrote first value... } TestData d; int oldValue = 0; int newValue; do { { //Lock::Locker scope(&TestLock); d = TestDataUpdater.GetState(); } newValue = d.ReadAndCheckConsistency(oldValue); // Values should increase or stay the same! if (newValue < oldValue) { LogText("LocklessTest Fail - %d after %d; delta = %d\n", newValue, oldValue, newValue - oldValue); // OVR_ASSERT(0); } if (oldValue != newValue) { oldValue = newValue; if (oldValue % (TestIterations/30) == 0) { LogText("LocklessTest::Consumer - %5.2f%% done\n", 100.0f * (float)oldValue/(float)TestIterations); } } // Spin a while for (int j = 0; j< 300; j++) { Dummy3 = j; } } while (oldValue < (TestIterations * 99 / 100)); LogText("LocklessTest::Consumer::Run exiting.\n"); return 0; } }; //------------------------------------------------------------------------------------- class Producer : public Thread { virtual int Run() { LogText("LocklessTest::Producer::Run started.\n"); for (int testVal = 0; testVal < TestIterations; testVal++) { TestData d; d.Set(testVal); { //Lock::Locker scope(&TestLock); TestDataUpdater.SetState(d); } FirstItemWritten = true; // Spin a bit for(int j = 0; j < 1000; j++) { Dummy2 = j; } if (testVal % (TestIterations/30) == 0) { LogText("LocklessTest::Producer - %5.2f%% done\n", 100.0f * (float)testVal/(float)TestIterations); } } LogText("LocklessTest::Producer::Run exiting.\n"); return 0; } }; } // namespace LocklessTest void StartLocklessTest() { // These threads will release themselves once done Ptr producerThread = *new LocklessTest::Producer; Ptr consumerThread = *new LocklessTest::Consumer; producerThread->Start(); consumerThread->Start(); /* while (!producerThread->IsFinished() && consumerThread->IsFinished()) { Thread::MSleep(500); } */ // TBD: Cleanup } } // namespace OVR #endif // OVR_LOCKLESS_TEST \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Lockless.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Lockless.h new file mode 100644 index 0000000..9730a20 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Lockless.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Lockless.h Content : Lock-less classes for producer/consumer communication Created : November 9, 2013 Authors : John Carmack Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Lockless_h #define OVR_Lockless_h #include "OVR_Atomic.h" // Define this to compile-in Lockless test logic //#define OVR_LOCKLESS_TEST namespace OVR { // ***** LocklessUpdater // For single producer cases where you only care about the most recent update, not // necessarily getting every one that happens (vsync timing, SensorFusion updates). // // This is multiple consumer safe, but is currently only used with a single consumer. template class LocklessUpdater { public: LocklessUpdater() : UpdateBegin( 0 ), UpdateEnd( 0 ) {} T GetState() const { // Copy the state out, then retry with the alternate slot // if we determine that our copy may have been partially // stepped on by a new update. T state; int begin, end, final; for(;;) { // We are adding 0, only using these as atomic memory barriers, so it // is ok to cast off the const, allowing GetState() to remain const. end = UpdateEnd.ExchangeAdd_Sync(0); state = Slots[ end & 1 ]; begin = UpdateBegin.ExchangeAdd_Sync(0); if ( begin == end ) { break; } // The producer is potentially blocked while only having partially // written the update, so copy out the other slot. state = Slots[ (begin & 1) ^ 1 ]; final = UpdateBegin.ExchangeAdd_NoSync(0); if ( final == begin ) { break; } // The producer completed the last update and started a new one before // we got it copied out, so try fetching the current buffer again. } return state; } void SetState( T state ) { const int slot = UpdateBegin.ExchangeAdd_Sync(1) & 1; // Write to (slot ^ 1) because ExchangeAdd returns 'previous' value before add. Slots[slot ^ 1] = state; UpdateEnd.ExchangeAdd_Sync(1); } mutable AtomicInt UpdateBegin; mutable AtomicInt UpdateEnd; T Slots[2]; }; #ifdef OVR_LOCKLESS_TEST void StartLocklessTest(); #endif } // namespace OVR #endif // OVR_Lockless_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Log.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Log.cpp new file mode 100644 index 0000000..8ed9fbd --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Log.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Log.cpp Content : Logging support Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Log.h" #include "OVR_Std.h" #include #include #if defined(OVR_OS_WIN32) #include #elif defined(OVR_OS_ANDROID) #include #endif namespace OVR { // Global Log pointer. Log* volatile OVR_GlobalLog = 0; //----------------------------------------------------------------------------------- // ***** Log Implementation Log::~Log() { // Clear out global log if (this == OVR_GlobalLog) { // TBD: perhaps we should ASSERT if this happens before system shutdown? OVR_GlobalLog = 0; } } void Log::LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList) { if ((messageType & LoggingMask) == 0) return; #ifndef OVR_BUILD_DEBUG if (IsDebugMessage(messageType)) return; #endif char buffer[MaxLogBufferMessageSize]; FormatLog(buffer, MaxLogBufferMessageSize, messageType, fmt, argList); DefaultLogOutput(buffer, IsDebugMessage(messageType)); } void OVR::Log::LogMessage(LogMessageType messageType, const char* pfmt, ...) { va_list argList; va_start(argList, pfmt); LogMessageVarg(messageType, pfmt, argList); va_end(argList); } void Log::FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, const char* fmt, va_list argList) { bool addNewline = true; switch(messageType) { case Log_Error: OVR_strcpy(buffer, bufferSize, "Error: "); break; case Log_Debug: OVR_strcpy(buffer, bufferSize, "Debug: "); break; case Log_Assert: OVR_strcpy(buffer, bufferSize, "Assert: "); break; case Log_Text: buffer[0] = 0; addNewline = false; break; case Log_DebugText: buffer[0] = 0; addNewline = false; break; default: buffer[0] = 0; addNewline = false; break; } UPInt prefixLength = OVR_strlen(buffer); char *buffer2 = buffer + prefixLength; OVR_vsprintf(buffer2, bufferSize - prefixLength, fmt, argList); if (addNewline) OVR_strcat(buffer, bufferSize, "\n"); } void Log::DefaultLogOutput(const char* formattedText, bool debug) { #if defined(OVR_OS_WIN32) // Under Win32, output regular messages to console if it exists; debug window otherwise. static DWORD dummyMode; static bool hasConsole = (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE) && (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dummyMode)); if (!hasConsole || debug) { ::OutputDebugStringA(formattedText); } else { fputs(formattedText, stdout); } #elif defined(OVR_OS_ANDROID) __android_log_write(ANDROID_LOG_INFO, "OVR", formattedText); #else fputs(formattedText, stdout); #endif // Just in case. OVR_UNUSED2(formattedText, debug); } //static void Log::SetGlobalLog(Log *log) { OVR_GlobalLog = log; } //static Log* Log::GetGlobalLog() { // No global log by default? // if (!OVR_GlobalLog) // OVR_GlobalLog = GetDefaultLog(); return OVR_GlobalLog; } //static Log* Log::GetDefaultLog() { // Create default log pointer statically so that it can be used // even during startup. static Log defaultLog; return &defaultLog; } //----------------------------------------------------------------------------------- // ***** Global Logging functions #define OVR_LOG_FUNCTION_IMPL(Name) \ void Log##Name(const char* fmt, ...) \ { \ if (OVR_GlobalLog) \ { \ va_list argList; va_start(argList, fmt); \ OVR_GlobalLog->LogMessageVarg(Log_##Name, fmt, argList); \ va_end(argList); \ } \ } OVR_LOG_FUNCTION_IMPL(Text) OVR_LOG_FUNCTION_IMPL(Error) #ifdef OVR_BUILD_DEBUG OVR_LOG_FUNCTION_IMPL(DebugText) OVR_LOG_FUNCTION_IMPL(Debug) OVR_LOG_FUNCTION_IMPL(Assert) #endif } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Log.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Log.h new file mode 100644 index 0000000..8cd8a07 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Log.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_Log.h Content : Logging support Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Log_h #define OVR_Log_h #include "OVR_Types.h" #include namespace OVR { //----------------------------------------------------------------------------------- // ***** Logging Constants // LogMaskConstants defined bit mask constants that describe what log messages // should be displayed. enum LogMaskConstants { LogMask_Regular = 0x100, LogMask_Debug = 0x200, LogMask_None = 0, LogMask_All = LogMask_Regular|LogMask_Debug }; // LogMessageType describes the type of the log message, controls when it is // displayed and what prefix/suffix is given to it. Messages are subdivided into // regular and debug logging types. Debug logging is only generated in debug builds. // // Log_Text - General output text displayed without prefix or new-line. // Used in OVR libraries for general log flow messages // such as "Device Initialized". // // Log_Error - Error message output with "Error: %s\n", intended for // application/sample-level use only, in cases where an expected // operation failed. OVR libraries should not use this internally, // reporting status codes instead. // // Log_DebugText - Message without prefix or new lines; output in Debug build only. // // Log_Debug - Debug-build only message, formatted with "Debug: %s\n". // Intended to comment on incorrect API usage that doesn't lead // to crashes but can be avoided with proper use. // There is no Debug Error on purpose, since real errors should // be handled by API user. // // Log_Assert - Debug-build only message, formatted with "Assert: %s\n". // Intended for severe unrecoverable conditions in library // source code. Generated though OVR_ASSERT_MSG(c, "Text"). enum LogMessageType { // General Logging Log_Text = LogMask_Regular | 0, Log_Error = LogMask_Regular | 1, // "Error: %s\n". // Debug-only messages (not generated in release build) Log_DebugText = LogMask_Debug | 0, Log_Debug = LogMask_Debug | 1, // "Debug: %s\n". Log_Assert = LogMask_Debug | 2, // "Assert: %s\n". }; // LOG_VAARG_ATTRIBUTE macro, enforces printf-style fromatting for message types #ifdef __GNUC__ # define OVR_LOG_VAARG_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) #else # define OVR_LOG_VAARG_ATTRIBUTE(a,b) #endif //----------------------------------------------------------------------------------- // ***** Log // Log defines a base class interface that can be implemented to catch both // debug and runtime messages. // Debug logging can be overridden by calling Log::SetGlobalLog. class Log { friend class System; public: Log(unsigned logMask = LogMask_Debug) : LoggingMask(logMask) { } virtual ~Log(); // Log formating buffer size used by default LogMessageVarg. Longer strings are truncated. enum { MaxLogBufferMessageSize = 4096 }; unsigned GetLoggingMask() const { return LoggingMask; } void SetLoggingMask(unsigned logMask) { LoggingMask = logMask; } // This virtual function receives all the messages, // developers should override this function in order to do custom logging virtual void LogMessageVarg(LogMessageType messageType, const char* fmt, va_list argList); // Call the logging function with specific message type, with no type filtering. void LogMessage(LogMessageType messageType, const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(3,4); // Helper used by LogMessageVarg to format the log message, writing the resulting // string into buffer. It formats text based on fmt and appends prefix/new line // based on LogMessageType. static void FormatLog(char* buffer, unsigned bufferSize, LogMessageType messageType, const char* fmt, va_list argList); // Default log output implementation used by by LogMessageVarg. // Debug flag may be used to re-direct output on some platforms, but doesn't // necessarily disable it in release builds; that is the job of the called. static void DefaultLogOutput(const char* textBuffer, bool debug); // Determines if the specified message type is for debugging only. static bool IsDebugMessage(LogMessageType messageType) { return (messageType & LogMask_Debug) != 0; } // *** Global APIs // Global Log registration APIs. // - Global log is used for OVR_DEBUG messages. Set global log to null (0) // to disable all logging. static void SetGlobalLog(Log *log); static Log* GetGlobalLog(); // Returns default log singleton instance. static Log* GetDefaultLog(); // Applies logMask to the default log and returns a pointer to it. // By default, only Debug logging is enabled, so to avoid SDK generating console // messages in user app (those are always disabled in release build, // even if the flag is set). This function is useful in System constructor. static Log* ConfigureDefaultLog(unsigned logMask = LogMask_Debug) { Log* log = GetDefaultLog(); log->SetLoggingMask(logMask); return log; } private: // Logging mask described by LogMaskConstants. unsigned LoggingMask; }; //----------------------------------------------------------------------------------- // ***** Global Logging Functions and Debug Macros // These functions will output text to global log with semantics described by // their LogMessageType. void LogText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogError(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); #ifdef OVR_BUILD_DEBUG // Debug build only logging. void LogDebugText(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogDebug(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); void LogAssert(const char* fmt, ...) OVR_LOG_VAARG_ATTRIBUTE(1,2); // Macro to do debug logging, printf-style. // An extra set of set of parenthesis must be used around arguments, // as in: OVR_LOG_DEBUG(("Value %d", 2)). #define OVR_DEBUG_LOG(args) do { OVR::LogDebug args; } while(0) #define OVR_DEBUG_LOG_TEXT(args) do { OVR::LogDebugText args; } while(0) #define OVR_ASSERT_LOG(c, args) do { if (!(c)) { OVR::LogAssert args; OVR_DEBUG_BREAK; } } while(0) #else // If not in debug build, macros do nothing. #define OVR_DEBUG_LOG(args) ((void)0) #define OVR_DEBUG_LOG_TEXT(args) ((void)0) #define OVR_ASSERT_LOG(c, args) ((void)0) #endif } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Math.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Math.cpp new file mode 100644 index 0000000..01cbece --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Math.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Math.h Content : Implementation of 3D primitives such as vectors, matrices. Created : September 4, 2012 Authors : Andrew Reisse, Michael Antonov, Anna Yershova Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Math.h" #include "OVR_Log.h" #include namespace OVR { //------------------------------------------------------------------------------------- // ***** Math // Single-precision Math constants class. const float Math::Pi = 3.1415926f; const float Math::TwoPi = 3.1415926f * 2; const float Math::PiOver2 = 3.1415926f / 2.0f; const float Math::PiOver4 = 3.1415926f / 4.0f; const float Math::E = 2.7182818f; const float Math::MaxValue = FLT_MAX; const float Math::MinPositiveValue = FLT_MIN; const float Math::RadToDegreeFactor = 360.0f / Math::TwoPi; const float Math::DegreeToRadFactor = Math::TwoPi / 360.0f; const float Math::Tolerance = 0.00001f; const float Math::SingularityRadius = 0.0000001f; // Use for Gimbal lock numerical problems // Double-precision Math constants class. const double Math::Pi = 3.14159265358979; const double Math::TwoPi = 3.14159265358979 * 2; const double Math::PiOver2 = 3.14159265358979 / 2.0; const double Math::PiOver4 = 3.14159265358979 / 4.0; const double Math::E = 2.71828182845905; const double Math::MaxValue = DBL_MAX; const double Math::MinPositiveValue = DBL_MIN; const double Math::RadToDegreeFactor = 360.0 / Math::TwoPi; const double Math::DegreeToRadFactor = Math::TwoPi / 360.0; const double Math::Tolerance = 0.00001; const double Math::SingularityRadius = 0.000000000001; // Use for Gimbal lock numerical problems //------------------------------------------------------------------------------------- // ***** Matrix4 template<> const Matrix4 Matrix4::IdentityValue = Matrix4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); template<> const Matrix4 Matrix4::IdentityValue = Matrix4(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0); } // Namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Math.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Math.h new file mode 100644 index 0000000..a60d5c6 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Math.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Math.h Content : Implementation of 3D primitives such as vectors, matrices. Created : September 4, 2012 Authors : Andrew Reisse, Michael Antonov, Steve LaValle, Anna Yershova, Max Katsev, Dov Katz Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Math_h #define OVR_Math_h #include #include #include #include "OVR_Types.h" #include "OVR_RefCount.h" #include "OVR_Std.h" #include "OVR_Alg.h" namespace OVR { //------------------------------------------------------------------------------------- // ***** Constants for 3D world/axis definitions. // Definitions of axes for coordinate and rotation conversions. enum Axis { Axis_X = 0, Axis_Y = 1, Axis_Z = 2 }; // RotateDirection describes the rotation direction around an axis, interpreted as follows: // CW - Clockwise while looking "down" from positive axis towards the origin. // CCW - Counter-clockwise while looking from the positive axis towards the origin, // which is in the negative axis direction. // CCW is the default for the RHS coordinate system. Oculus standard RHS coordinate // system defines Y up, X right, and Z back (pointing out from the screen). In this // system Rotate_CCW around Z will specifies counter-clockwise rotation in XY plane. enum RotateDirection { Rotate_CCW = 1, Rotate_CW = -1 }; // Constants for right handed and left handed coordinate systems enum HandedSystem { Handed_R = 1, Handed_L = -1 }; // AxisDirection describes which way the coordinate axis points. Used by WorldAxes. enum AxisDirection { Axis_Up = 2, Axis_Down = -2, Axis_Right = 1, Axis_Left = -1, Axis_In = 3, Axis_Out = -3 }; struct WorldAxes { AxisDirection XAxis, YAxis, ZAxis; WorldAxes(AxisDirection x, AxisDirection y, AxisDirection z) : XAxis(x), YAxis(y), ZAxis(z) { OVR_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x));} }; } // namespace OVR //------------------------------------------------------------------------------------// // ***** C Compatibility Types // These declarations are used to support conversion between C types used in // LibOVR C interfaces and their C++ versions. As an example, they allow passing // Vector3f into a function that expects ovrVector3f. typedef struct ovrQuatf_ ovrQuatf; typedef struct ovrQuatd_ ovrQuatd; typedef struct ovrSizei_ ovrSizei; typedef struct ovrSizef_ ovrSizef; typedef struct ovrRecti_ ovrRecti; typedef struct ovrVector2i_ ovrVector2i; typedef struct ovrVector2f_ ovrVector2f; typedef struct ovrVector3f_ ovrVector3f; typedef struct ovrVector3d_ ovrVector3d; typedef struct ovrMatrix3d_ ovrMatrix3d; typedef struct ovrMatrix4f_ ovrMatrix4f; typedef struct ovrPosef_ ovrPosef; typedef struct ovrPosed_ ovrPosed; typedef struct ovrPoseStatef_ ovrPoseStatef; typedef struct ovrPoseStated_ ovrPoseStated; namespace OVR { // Forward-declare our templates. template class Quat; template class Size; template class Rect; template class Vector2; template class Vector3; template class Matrix3; template class Matrix4; template class Transform; template class PoseState; // CompatibleTypes::Type is used to lookup a compatible C-version of a C++ class. template struct CompatibleTypes { // Declaration here seems necessary for MSVC; specializations are // used instead. typedef float Type; }; // Specializations providing CompatibleTypes::Type value. template<> struct CompatibleTypes > { typedef ovrQuatf Type; }; template<> struct CompatibleTypes > { typedef ovrQuatd Type; }; template<> struct CompatibleTypes > { typedef ovrMatrix3d Type; }; template<> struct CompatibleTypes > { typedef ovrMatrix4f Type; }; template<> struct CompatibleTypes > { typedef ovrSizei Type; }; template<> struct CompatibleTypes > { typedef ovrSizef Type; }; template<> struct CompatibleTypes > { typedef ovrRecti Type; }; template<> struct CompatibleTypes > { typedef ovrVector2i Type; }; template<> struct CompatibleTypes > { typedef ovrVector2f Type; }; template<> struct CompatibleTypes > { typedef ovrVector3f Type; }; template<> struct CompatibleTypes > { typedef ovrVector3d Type; }; template<> struct CompatibleTypes > { typedef ovrPosef Type; }; template<> struct CompatibleTypes > { typedef ovrPoseStatef Type; }; template<> struct CompatibleTypes > { typedef ovrPosed Type; }; template<> struct CompatibleTypes > { typedef ovrPoseStated Type; }; //------------------------------------------------------------------------------------// // ***** Math // // Math class contains constants and functions. This class is a template specialized // per type, with Math and Math being distinct. template class Math { public: // By default, support explicit conversion to float. This allows Vector2 to // compile, for example. typedef float OtherFloatType; }; // Single-precision Math constants class. template<> class Math { public: static const float Pi; static const float TwoPi; static const float PiOver2; static const float PiOver4; static const float E; static const float MaxValue; // Largest positive float Value static const float MinPositiveValue; // Smallest possible positive value static const float RadToDegreeFactor; static const float DegreeToRadFactor; static const float Tolerance; // 0.00001f; static const float SingularityRadius; // 0.0000001f for Gimbal lock numerical problems // Used to support direct conversions in template classes. typedef double OtherFloatType; }; // Double-precision Math constants class. template<> class Math { public: static const double Pi; static const double TwoPi; static const double PiOver2; static const double PiOver4; static const double E; static const double MaxValue; // Largest positive double Value static const double MinPositiveValue; // Smallest possible positive value static const double RadToDegreeFactor; static const double DegreeToRadFactor; static const double Tolerance; // 0.00001; static const double SingularityRadius; // 0.000000000001 for Gimbal lock numerical problems typedef float OtherFloatType; }; typedef Math Mathf; typedef Math Mathd; // Conversion functions between degrees and radians template T RadToDegree(T rads) { return rads * Math::RadToDegreeFactor; } template T DegreeToRad(T rads) { return rads * Math::DegreeToRadFactor; } // Numerically stable acos function template T Acos(T val) { if (val > T(1)) return T(0); else if (val < T(-1)) return Math::Pi; else return acos(val); }; // Numerically stable asin function template T Asin(T val) { if (val > T(1)) return Math::PiOver2; else if (val < T(-1)) return Math::PiOver2 * T(3); else return asin(val); }; #ifdef OVR_CC_MSVC inline int isnan(double x) { return _isnan(x); }; #endif template class Quat; //------------------------------------------------------------------------------------- // ***** Vector2<> // Vector2f (Vector2d) represents a 2-dimensional vector or point in space, // consisting of coordinates x and y template class Vector2 { public: T x, y; Vector2() : x(0), y(0) { } Vector2(T x_, T y_) : x(x_), y(y_) { } explicit Vector2(T s) : x(s), y(s) { } explicit Vector2(const Vector2::OtherFloatType> &src) : x((T)src.x), y((T)src.y) { } // C-interop support. typedef typename CompatibleTypes >::Type CompatibleType; Vector2(const CompatibleType& s) : x(s.x), y(s.y) { } operator const CompatibleType& () const { OVR_COMPILER_ASSERT(sizeof(Vector2) == sizeof(CompatibleType)); return reinterpret_cast(*this); } bool operator== (const Vector2& b) const { return x == b.x && y == b.y; } bool operator!= (const Vector2& b) const { return x != b.x || y != b.y; } Vector2 operator+ (const Vector2& b) const { return Vector2(x + b.x, y + b.y); } Vector2& operator+= (const Vector2& b) { x += b.x; y += b.y; return *this; } Vector2 operator- (const Vector2& b) const { return Vector2(x - b.x, y - b.y); } Vector2& operator-= (const Vector2& b) { x -= b.x; y -= b.y; return *this; } Vector2 operator- () const { return Vector2(-x, -y); } // Scalar multiplication/division scales vector. Vector2 operator* (T s) const { return Vector2(x*s, y*s); } Vector2& operator*= (T s) { x *= s; y *= s; return *this; } Vector2 operator/ (T s) const { T rcp = T(1)/s; return Vector2(x*rcp, y*rcp); } Vector2& operator/= (T s) { T rcp = T(1)/s; x *= rcp; y *= rcp; return *this; } static Vector2 Min(const Vector2& a, const Vector2& b) { return Vector2((a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y); } static Vector2 Max(const Vector2& a, const Vector2& b) { return Vector2((a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y); } // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. bool Compare(const Vector2&b, T tolerance = Mathf::Tolerance) { return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance); } // Entrywise product of two vectors Vector2 EntrywiseMultiply(const Vector2& b) const { return Vector2(x * b.x, y * b.y);} // Multiply and divide operators do entry-wise math. Used Dot() for dot product. Vector2 operator* (const Vector2& b) const { return Vector2(x * b.x, y * b.y); } Vector2 operator/ (const Vector2& b) const { return Vector2(x / b.x, y / b.y); } // Dot product // Used to calculate angle q between two vectors among other things, // as (A dot B) = |a||b|cos(q). T Dot(const Vector2& b) const { return x*b.x + y*b.y; } // Returns the angle from this vector to b, in radians. T Angle(const Vector2& b) const { T div = LengthSq()*b.LengthSq(); OVR_ASSERT(div != T(0)); T result = Acos((this->Dot(b))/sqrt(div)); return result; } // Return Length of the vector squared. T LengthSq() const { return (x * x + y * y); } // Return vector length. T Length() const { return sqrt(LengthSq()); } // Returns squared distance between two points represented by vectors. T DistanceSq(Vector2& b) const { return (*this - b).LengthSq(); } // Returns distance between two points represented by vectors. T Distance(Vector2& b) const { return (*this - b).Length(); } // Determine if this a unit vector. bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } // Normalize, convention vector length to 1. void Normalize() { T l = Length(); OVR_ASSERT(l != T(0)); *this /= l; } // Returns normalized (unit) version of the vector without modifying itself. Vector2 Normalized() const { T l = Length(); OVR_ASSERT(l != T(0)); return *this / l; } // Linearly interpolates from this vector to another. // Factor should be between 0.0 and 1.0, with 0 giving full value to this. Vector2 Lerp(const Vector2& b, T f) const { return *this*(T(1) - f) + b*f; } // Projects this vector onto the argument; in other words, // A.Project(B) returns projection of vector A onto B. Vector2 ProjectTo(const Vector2& b) const { T l2 = b.LengthSq(); OVR_ASSERT(l2 != T(0)); return b * ( Dot(b) / l2 ); } }; typedef Vector2 Vector2f; typedef Vector2 Vector2d; typedef Vector2 Vector2i; //------------------------------------------------------------------------------------- // ***** Vector3<> - 3D vector of {x, y, z} // // Vector3f (Vector3d) represents a 3-dimensional vector or point in space, // consisting of coordinates x, y and z. template class Vector3 { public: T x, y, z; Vector3() : x(0), y(0), z(0) { } Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) { } explicit Vector3(T s) : x(s), y(s), z(s) { } explicit Vector3(const Vector3::OtherFloatType> &src) : x((T)src.x), y((T)src.y), z((T)src.z) { } // C-interop support. typedef typename CompatibleTypes >::Type CompatibleType; Vector3(const CompatibleType& s) : x(s.x), y(s.y), z(s.z) { } operator const CompatibleType& () const { OVR_COMPILER_ASSERT(sizeof(Vector3) == sizeof(CompatibleType)); return reinterpret_cast(*this); } bool operator== (const Vector3& b) const { return x == b.x && y == b.y && z == b.z; } bool operator!= (const Vector3& b) const { return x != b.x || y != b.y || z != b.z; } Vector3 operator+ (const Vector3& b) const { return Vector3(x + b.x, y + b.y, z + b.z); } Vector3& operator+= (const Vector3& b) { x += b.x; y += b.y; z += b.z; return *this; } Vector3 operator- (const Vector3& b) const { return Vector3(x - b.x, y - b.y, z - b.z); } Vector3& operator-= (const Vector3& b) { x -= b.x; y -= b.y; z -= b.z; return *this; } Vector3 operator- () const { return Vector3(-x, -y, -z); } // Scalar multiplication/division scales vector. Vector3 operator* (T s) const { return Vector3(x*s, y*s, z*s); } Vector3& operator*= (T s) { x *= s; y *= s; z *= s; return *this; } Vector3 operator/ (T s) const { T rcp = T(1)/s; return Vector3(x*rcp, y*rcp, z*rcp); } Vector3& operator/= (T s) { T rcp = T(1)/s; x *= rcp; y *= rcp; z *= rcp; return *this; } static Vector3 Min(const Vector3& a, const Vector3& b) { return Vector3((a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y, (a.z < b.z) ? a.z : b.z); } static Vector3 Max(const Vector3& a, const Vector3& b) { return Vector3((a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y, (a.z > b.z) ? a.z : b.z); } // Compare two vectors for equality with tolerance. Returns true if vectors match withing tolerance. bool Compare(const Vector3&b, T tolerance = Mathf::Tolerance) { return (fabs(b.x-x) < tolerance) && (fabs(b.y-y) < tolerance) && (fabs(b.z-z) < tolerance); } T& operator[] (int idx) { OVR_ASSERT(0 <= idx && idx < 3); return *(&x + idx); } const T& operator[] (int idx) const { OVR_ASSERT(0 <= idx && idx < 3); return *(&x + idx); } // Entrywise product of two vectors Vector3 EntrywiseMultiply(const Vector3& b) const { return Vector3(x * b.x, y * b.y, z * b.z);} // Multiply and divide operators do entry-wise math Vector3 operator* (const Vector3& b) const { return Vector3(x * b.x, y * b.y, z * b.z); } Vector3 operator/ (const Vector3& b) const { return Vector3(x / b.x, y / b.y, z / b.z); } // Dot product // Used to calculate angle q between two vectors among other things, // as (A dot B) = |a||b|cos(q). T Dot(const Vector3& b) const { return x*b.x + y*b.y + z*b.z; } // Compute cross product, which generates a normal vector. // Direction vector can be determined by right-hand rule: Pointing index finder in // direction a and middle finger in direction b, thumb will point in a.Cross(b). Vector3 Cross(const Vector3& b) const { return Vector3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); } // Returns the angle from this vector to b, in radians. T Angle(const Vector3& b) const { T div = LengthSq()*b.LengthSq(); OVR_ASSERT(div != T(0)); T result = Acos((this->Dot(b))/sqrt(div)); return result; } // Return Length of the vector squared. T LengthSq() const { return (x * x + y * y + z * z); } // Return vector length. T Length() const { return sqrt(LengthSq()); } // Returns squared distance between two points represented by vectors. T DistanceSq(Vector3 const& b) const { return (*this - b).LengthSq(); } // Returns distance between two points represented by vectors. T Distance(Vector3 const& b) const { return (*this - b).Length(); } // Determine if this a unit vector. bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } // Normalize, convention vector length to 1. void Normalize() { T l = Length(); OVR_ASSERT(l != T(0)); *this /= l; } // Returns normalized (unit) version of the vector without modifying itself. Vector3 Normalized() const { T l = Length(); OVR_ASSERT(l != T(0)); return *this / l; } // Linearly interpolates from this vector to another. // Factor should be between 0.0 and 1.0, with 0 giving full value to this. Vector3 Lerp(const Vector3& b, T f) const { return *this*(T(1) - f) + b*f; } // Projects this vector onto the argument; in other words, // A.Project(B) returns projection of vector A onto B. Vector3 ProjectTo(const Vector3& b) const { T l2 = b.LengthSq(); OVR_ASSERT(l2 != T(0)); return b * ( Dot(b) / l2 ); } // Projects this vector onto a plane defined by a normal vector Vector3 ProjectToPlane(const Vector3& normal) const { return *this - this->ProjectTo(normal); } }; typedef Vector3 Vector3f; typedef Vector3 Vector3d; typedef Vector3 Vector3i; // JDC: this was defined in Render_Device.h, I moved it here, but it // needs to be fleshed out like the other Vector types. // // A vector with a dummy w component for alignment in uniform buffers (and for float colors). // The w component is not used in any calculations. struct Vector4f : public Vector3f { float w; Vector4f() : w(1) {} Vector4f(const Vector3f& v) : Vector3f(v), w(1) {} Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {} }; //------------------------------------------------------------------------------------- // ***** Size // Size class represents 2D size with Width, Height components. // Used to describe distentions of render targets, etc. template class Size { public: T w, h; Size() : w(0), h(0) { } Size(T w_, T h_) : w(w_), h(h_) { } explicit Size(T s) : w(s), h(s) { } explicit Size(const Size::OtherFloatType> &src) : w((T)src.w), h((T)src.h) { } // C-interop support. typedef typename CompatibleTypes >::Type CompatibleType; Size(const CompatibleType& s) : w(s.w), h(s.h) { } operator const CompatibleType& () const { OVR_COMPILER_ASSERT(sizeof(Size) == sizeof(CompatibleType)); return reinterpret_cast(*this); } bool operator== (const Size& b) const { return w == b.w && h == b.h; } bool operator!= (const Size& b) const { return w != b.w || h != b.h; } Size operator+ (const Size& b) const { return Size(w + b.w, h + b.h); } Size& operator+= (const Size& b) { w += b.w; h += b.h; return *this; } Size operator- (const Size& b) const { return Size(w - b.w, h - b.h); } Size& operator-= (const Size& b) { w -= b.w; h -= b.h; return *this; } Size operator- () const { return Size(-w, -h); } Size operator* (const Size& b) const { return Size(w * b.w, h * b.h); } Size& operator*= (const Size& b) { w *= b.w; h *= b.h; return *this; } Size operator/ (const Size& b) const { return Size(w / b.w, h / b.h); } Size& operator/= (const Size& b) { w /= b.w; h /= b.h; return *this; } // Scalar multiplication/division scales both components. Size operator* (T s) const { return Size(w*s, h*s); } Size& operator*= (T s) { w *= s; h *= s; return *this; } Size operator/ (T s) const { return Size(w/s, h/s); } Size& operator/= (T s) { w /= s; h /= s; return *this; } static Size Min(const Size& a, const Size& b) { return Size((a.w < b.w) ? a.w : b.w, (a.h < b.h) ? a.h : b.h); } static Size Max(const Size& a, const Size& b) { return Size((a.w > b.w) ? a.w : b.w, (a.h > b.h) ? a.h : b.h); } T Area() const { return w * h; } inline Vector2 ToVector() const { return Vector2(w, h); } }; typedef Size Sizei; typedef Size Sizeu; typedef Size Sizef; typedef Size Sized; //----------------------------------------------------------------------------------- // ***** Rect // Rect describes a rectangular area for rendering, that includes position and size. template class Rect { public: T x, y; T w, h; Rect() { } Rect(T x1, T y1, T w1, T h1) : x(x1), y(y1), w(w1), h(h1) { } Rect(const Vector2& pos, const Size& sz) : x(pos.x), y(pos.y), w(sz.w), h(sz.h) { } Rect(const Size& sz) : x(0), y(0), w(sz.w), h(sz.h) { } // C-interop support. typedef typename CompatibleTypes >::Type CompatibleType; Rect(const CompatibleType& s) : x(s.Pos.x), y(s.Pos.y), w(s.Size.w), h(s.Size.h) { } operator const CompatibleType& () const { OVR_COMPILER_ASSERT(sizeof(Rect) == sizeof(CompatibleType)); return reinterpret_cast(*this); } Vector2 GetPos() const { return Vector2(x, y); } Size GetSize() const { return Size(w, h); } void SetPos(const Vector2& pos) { x = pos.x; y = pos.y; } void SetSize(const Size& sz) { w = sz.w; h = sz.h; } bool operator == (const Rect& vp) const { return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); } bool operator != (const Rect& vp) const { return !operator == (vp); } }; typedef Rect Recti; //-------------------------------------------------------------------------------------// // ***** Quat // // Quatf represents a quaternion class used for rotations. // // Quaternion multiplications are done in right-to-left order, to match the // behavior of matrices. template class Quat { public: // w + Xi + Yj + Zk T x, y, z, w; Quat() : x(0), y(0), z(0), w(1) { } Quat(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) { } explicit Quat(const Quat::OtherFloatType> &src) : x((T)src.x), y((T)src.y), z((T)src.z), w((T)src.w) { } // C-interop support. Quat(const typename CompatibleTypes >::Type& s) : x(s.x), y(s.y), z(s.z), w(s.w) { } operator typename CompatibleTypes >::Type () const { typename CompatibleTypes >::Type result; result.x = x; result.y = y; result.z = z; result.w = w; return result; } // Constructs quaternion for rotation around the axis by an angle. Quat(const Vector3& axis, T angle) { // Make sure we don't divide by zero. if (axis.LengthSq() == 0) { // Assert if the axis is zero, but the angle isn't OVR_ASSERT(angle == 0); x = 0; y = 0; z = 0; w = 1; return; } Vector3 unitAxis = axis.Normalized(); T sinHalfAngle = sin(angle * T(0.5)); w = cos(angle * T(0.5)); x = unitAxis.x * sinHalfAngle; y = unitAxis.y * sinHalfAngle; z = unitAxis.z * sinHalfAngle; } // Constructs quaternion for rotation around one of the coordinate axis by an angle. Quat(Axis A, T angle, RotateDirection d = Rotate_CCW, HandedSystem s = Handed_R) { T sinHalfAngle = s * d *sin(angle * T(0.5)); T v[3]; v[0] = v[1] = v[2] = T(0); v[A] = sinHalfAngle; w = cos(angle * T(0.5)); x = v[0]; y = v[1]; z = v[2]; } // Compute axis and angle from quaternion void GetAxisAngle(Vector3* axis, T* angle) const { if ( x*x + y*y + z*z > Math::Tolerance * Math::Tolerance ) { *axis = Vector3(x, y, z).Normalized(); *angle = 2 * Acos(w); if (*angle > Math::Pi) // Reduce the magnitude of the angle, if necessary { *angle = Math::TwoPi - *angle; *axis = *axis * (-1); } } else { *axis = Vector3(1, 0, 0); *angle= 0; } } // Constructs the quaternion from a rotation matrix explicit Quat(const Matrix4& m) { T trace = m.M[0][0] + m.M[1][1] + m.M[2][2]; // In almost all cases, the first part is executed. // However, if the trace is not positive, the other // cases arise. if (trace > T(0)) { T s = sqrt(trace + T(1)) * T(2); // s=4*qw w = T(0.25) * s; x = (m.M[2][1] - m.M[1][2]) / s; y = (m.M[0][2] - m.M[2][0]) / s; z = (m.M[1][0] - m.M[0][1]) / s; } else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2])) { T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2); w = (m.M[2][1] - m.M[1][2]) / s; x = T(0.25) * s; y = (m.M[0][1] + m.M[1][0]) / s; z = (m.M[2][0] + m.M[0][2]) / s; } else if (m.M[1][1] > m.M[2][2]) { T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy w = (m.M[0][2] - m.M[2][0]) / s; x = (m.M[0][1] + m.M[1][0]) / s; y = T(0.25) * s; z = (m.M[1][2] + m.M[2][1]) / s; } else { T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz w = (m.M[1][0] - m.M[0][1]) / s; x = (m.M[0][2] + m.M[2][0]) / s; y = (m.M[1][2] + m.M[2][1]) / s; z = T(0.25) * s; } } // Constructs the quaternion from a rotation matrix explicit Quat(const Matrix3& m) { T trace = m.M[0][0] + m.M[1][1] + m.M[2][2]; // In almost all cases, the first part is executed. // However, if the trace is not positive, the other // cases arise. if (trace > T(0)) { T s = sqrt(trace + T(1)) * T(2); // s=4*qw w = T(0.25) * s; x = (m.M[2][1] - m.M[1][2]) / s; y = (m.M[0][2] - m.M[2][0]) / s; z = (m.M[1][0] - m.M[0][1]) / s; } else if ((m.M[0][0] > m.M[1][1])&&(m.M[0][0] > m.M[2][2])) { T s = sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2]) * T(2); w = (m.M[2][1] - m.M[1][2]) / s; x = T(0.25) * s; y = (m.M[0][1] + m.M[1][0]) / s; z = (m.M[2][0] + m.M[0][2]) / s; } else if (m.M[1][1] > m.M[2][2]) { T s = sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2]) * T(2); // S=4*qy w = (m.M[0][2] - m.M[2][0]) / s; x = (m.M[0][1] + m.M[1][0]) / s; y = T(0.25) * s; z = (m.M[1][2] + m.M[2][1]) / s; } else { T s = sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1]) * T(2); // S=4*qz w = (m.M[1][0] - m.M[0][1]) / s; x = (m.M[0][2] + m.M[2][0]) / s; y = (m.M[1][2] + m.M[2][1]) / s; z = T(0.25) * s; } } bool operator== (const Quat& b) const { return x == b.x && y == b.y && z == b.z && w == b.w; } bool operator!= (const Quat& b) const { return x != b.x || y != b.y || z != b.z || w != b.w; } Quat operator+ (const Quat& b) const { return Quat(x + b.x, y + b.y, z + b.z, w + b.w); } Quat& operator+= (const Quat& b) { w += b.w; x += b.x; y += b.y; z += b.z; return *this; } Quat operator- (const Quat& b) const { return Quat(x - b.x, y - b.y, z - b.z, w - b.w); } Quat& operator-= (const Quat& b) { w -= b.w; x -= b.x; y -= b.y; z -= b.z; return *this; } Quat operator* (T s) const { return Quat(x * s, y * s, z * s, w * s); } Quat& operator*= (T s) { w *= s; x *= s; y *= s; z *= s; return *this; } Quat operator/ (T s) const { T rcp = T(1)/s; return Quat(x * rcp, y * rcp, z * rcp, w *rcp); } Quat& operator/= (T s) { T rcp = T(1)/s; w *= rcp; x *= rcp; y *= rcp; z *= rcp; return *this; } // Get Imaginary part vector Vector3 Imag() const { return Vector3(x,y,z); } // Get quaternion length. T Length() const { return sqrt(LengthSq()); } // Get quaternion length squared. T LengthSq() const { return (x * x + y * y + z * z + w * w); } // Simple Euclidean distance in R^4 (not SLERP distance, but at least respects Haar measure) T Distance(const Quat& q) const { T d1 = (*this - q).Length(); T d2 = (*this + q).Length(); // Antipodal point check return (d1 < d2) ? d1 : d2; } T DistanceSq(const Quat& q) const { T d1 = (*this - q).LengthSq(); T d2 = (*this + q).LengthSq(); // Antipodal point check return (d1 < d2) ? d1 : d2; } T Dot(const Quat& q) const { return x * q.x + y * q.y + z * q.z + w * q.w; } // Angle between two quaternions in radians T Angle(const Quat& q) const { return 2 * Acos(Alg::Abs(Dot(q))); } // Normalize bool IsNormalized() const { return fabs(LengthSq() - T(1)) < Math::Tolerance; } void Normalize() { T l = Length(); OVR_ASSERT(l != T(0)); *this /= l; } Quat Normalized() const { T l = Length(); OVR_ASSERT(l != T(0)); return *this / l; } // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. Quat Conj() const { return Quat(-x, -y, -z, w); } // Quaternion multiplication. Combines quaternion rotations, performing the one on the // right hand side first. Quat operator* (const Quat& b) const { return Quat(w * b.x + x * b.w + y * b.z - z * b.y, w * b.y - x * b.z + y * b.w + z * b.x, w * b.z + x * b.y - y * b.x + z * b.w, w * b.w - x * b.x - y * b.y - z * b.z); } // // this^p normalized; same as rotating by this p times. Quat PowNormalized(T p) const { Vector3 v; T a; GetAxisAngle(&v, &a); return Quat(v, a * p); } // Normalized linear interpolation of quaternions Quat Nlerp(const Quat& other, T a) { T sign = (Dot(other) >= 0) ? 1 : -1; return (*this * sign * a + other * (1-a)).Normalized(); } // Rotate transforms vector in a manner that matches Matrix rotations (counter-clockwise, // assuming negative direction of the axis). Standard formula: q(t) * V * q(t)^-1. Vector3 Rotate(const Vector3& v) const { return ((*this * Quat(v.x, v.y, v.z, T(0))) * Inverted()).Imag(); } // Inversed quaternion rotates in the opposite direction. Quat Inverted() const { return Quat(-x, -y, -z, w); } // Sets this quaternion to the one rotates in the opposite direction. void Invert() { *this = Quat(-x, -y, -z, w); } // GetEulerAngles extracts Euler angles from the quaternion, in the specified order of // axis rotations and the specified coordinate system. Right-handed coordinate system // is the default, with CCW rotations while looking in the negative axis direction. // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A3 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void GetEulerAngles(T *a, T *b, T *c) const { OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); T Q[3] = { x, y, z }; //Quaternion components x,y,z T ww = w*w; T Q11 = Q[A1]*Q[A1]; T Q22 = Q[A2]*Q[A2]; T Q33 = Q[A3]*Q[A3]; T psign = T(-1); // Determine whether even permutation if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) psign = T(1); T s2 = psign * T(2) * (psign*w*Q[A2] + Q[A1]*Q[A3]); if (s2 < T(-1) + Math::SingularityRadius) { // South pole singularity *a = T(0); *b = -S*D*Math::PiOver2; *c = S*D*atan2(T(2)*(psign*Q[A1]*Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33 ); } else if (s2 > T(1) - Math::SingularityRadius) { // North pole singularity *a = T(0); *b = S*D*Math::PiOver2; *c = S*D*atan2(T(2)*(psign*Q[A1]*Q[A2] + w*Q[A3]), ww + Q22 - Q11 - Q33); } else { *a = -S*D*atan2(T(-2)*(w*Q[A1] - psign*Q[A2]*Q[A3]), ww + Q33 - Q11 - Q22); *b = S*D*asin(s2); *c = S*D*atan2(T(2)*(w*Q[A3] - psign*Q[A1]*Q[A2]), ww + Q11 - Q22 - Q33); } return; } template void GetEulerAngles(T *a, T *b, T *c) const { GetEulerAngles(a, b, c); } template void GetEulerAngles(T *a, T *b, T *c) const { GetEulerAngles(a, b, c); } // GetEulerAnglesABA extracts Euler angles from the quaternion, in the specified order of // axis rotations and the specified coordinate system. Right-handed coordinate system // is the default, with CCW rotations while looking in the negative axis direction. // Here a,b,c, are the Yaw/Pitch/Roll angles to be returned. // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A1 // Rotations are CCW or CW (D) in LH or RH coordinate system (S) template void GetEulerAnglesABA(T *a, T *b, T *c) const { OVR_COMPILER_ASSERT(A1 != A2); T Q[3] = {x, y, z}; // Quaternion components // Determine the missing axis that was not supplied int m = 3 - A1 - A2; T ww = w*w; T Q11 = Q[A1]*Q[A1]; T Q22 = Q[A2]*Q[A2]; T Qmm = Q[m]*Q[m]; T psign = T(-1); if ((A1 + 1) % 3 == A2) // Determine whether even permutation { psign = T(1); } T c2 = ww + Q11 - Q22 - Qmm; if (c2 < T(-1) + Math::SingularityRadius) { // South pole singularity *a = T(0); *b = S*D*Math::Pi; *c = S*D*atan2( T(2)*(w*Q[A1] - psign*Q[A2]*Q[m]), ww + Q22 - Q11 - Qmm); } else if (c2 > T(1) - Math::SingularityRadius) { // North pole singularity *a = T(0); *b = T(0); *c = S*D*atan2( T(2)*(w*Q[A1] - psign*Q[A2]*Q[m]), ww + Q22 - Q11 - Qmm); } else { *a = S*D*atan2( psign*w*Q[m] + Q[A1]*Q[A2], w*Q[A2] -psign*Q[A1]*Q[m]); *b = S*D*acos(c2); *c = S*D*atan2( -psign*w*Q[m] + Q[A1]*Q[A2], w*Q[A2] + psign*Q[A1]*Q[m]); } return; } }; typedef Quat Quatf; typedef Quat Quatd; //------------------------------------------------------------------------------------- // ***** Pose // Position and orientation combined. template class Transform { public: typedef typename CompatibleTypes >::Type CompatibleType; Transform() { } Transform(const Quat& orientation, const Vector3& pos) : Rotation(orientation), Translation(pos) { } Transform(const Transform& s) : Rotation(s.Rotation), Translation(s.Translation) { } Transform(const CompatibleType& s) : Rotation(s.Orientation), Translation(s.Position) { } explicit Transform(const Transform::OtherFloatType> &s) : Rotation(s.Rotation), Translation(s.Translation) { } operator typename CompatibleTypes >::Type () const { typename CompatibleTypes >::Type result; result.Orientation = Rotation; result.Position = Translation; return result; } Quat Rotation; Vector3 Translation; Vector3 Rotate(const Vector3& v) const { return Rotation.Rotate(v); } Vector3 Translate(const Vector3& v) const { return v + Translation; } Vector3 Apply(const Vector3& v) const { return Translate(Rotate(v)); } Transform operator*(const Transform& other) const { return Transform(Rotation * other.Rotation, Apply(other.Translation)); } PoseState operator*(const PoseState& poseState) const { PoseState result; result.Pose = (*this) * poseState.Pose; result.LinearVelocity = this->Rotate(poseState.LinearVelocity); result.LinearAcceleration = this->Rotate(poseState.LinearAcceleration); result.AngularVelocity = this->Rotate(poseState.AngularVelocity); result.AngularAcceleration = this->Rotate(poseState.AngularAcceleration); return result; } Transform Inverted() const { Quat inv = Rotation.Inverted(); return Transform(inv, inv.Rotate(-Translation)); } }; typedef Transform Transformf; typedef Transform Transformd; //------------------------------------------------------------------------------------- // ***** Matrix4 // // Matrix4 is a 4x4 matrix used for 3d transformations and projections. // Translation stored in the last column. // The matrix is stored in row-major order in memory, meaning that values // of the first row are stored before the next one. // // The arrangement of the matrix is chosen to be in Right-Handed // coordinate system and counterclockwise rotations when looking down // the axis // // Transformation Order: // - Transformations are applied from right to left, so the expression // M1 * M2 * M3 * V means that the vector V is transformed by M3 first, // followed by M2 and M1. // // Coordinate system: Right Handed // // Rotations: Counterclockwise when looking down the axis. All angles are in radians. // // | sx 01 02 tx | // First column (sx, 10, 20): Axis X basis vector. // | 10 sy 12 ty | // Second column (01, sy, 21): Axis Y basis vector. // | 20 21 sz tz | // Third columnt (02, 12, sz): Axis Z basis vector. // | 30 31 32 33 | // // The basis vectors are first three columns. template class Matrix4 { static const Matrix4 IdentityValue; public: T M[4][4]; enum NoInitType { NoInit }; // Construct with no memory initialization. Matrix4(NoInitType) { } // By default, we construct identity matrix. Matrix4() { SetIdentity(); } Matrix4(T m11, T m12, T m13, T m14, T m21, T m22, T m23, T m24, T m31, T m32, T m33, T m34, T m41, T m42, T m43, T m44) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = m14; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = m24; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = m34; M[3][0] = m41; M[3][1] = m42; M[3][2] = m43; M[3][3] = m44; } Matrix4(T m11, T m12, T m13, T m21, T m22, T m23, T m31, T m32, T m33) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[0][3] = 0; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[1][3] = 0; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; M[2][3] = 0; M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; } explicit Matrix4(const Quat& q) { T ww = q.w*q.w; T xx = q.x*q.x; T yy = q.y*q.y; T zz = q.z*q.z; M[0][0] = ww + xx - yy - zz; M[0][1] = 2 * (q.x*q.y - q.w*q.z); M[0][2] = 2 * (q.x*q.z + q.w*q.y); M[0][3] = 0; M[1][0] = 2 * (q.x*q.y + q.w*q.z); M[1][1] = ww - xx + yy - zz; M[1][2] = 2 * (q.y*q.z - q.w*q.x); M[1][3] = 0; M[2][0] = 2 * (q.x*q.z - q.w*q.y); M[2][1] = 2 * (q.y*q.z + q.w*q.x); M[2][2] = ww - xx - yy + zz; M[2][3] = 0; M[3][0] = 0; M[3][1] = 0; M[3][2] = 0; M[3][3] = 1; } explicit Matrix4(const Transform& p) { Matrix4 result(p.Rotation); result.SetTranslation(p.Translation); *this = result; } // C-interop support explicit Matrix4(const Matrix4::OtherFloatType> &src) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) M[i][j] = (T)src.M[i][j]; } // C-interop support. Matrix4(const typename CompatibleTypes >::Type& s) { OVR_COMPILER_ASSERT(sizeof(s) == sizeof(Matrix4)); memcpy(M, s.M, sizeof(M)); } operator typename CompatibleTypes >::Type () const { typename CompatibleTypes >::Type result; OVR_COMPILER_ASSERT(sizeof(result) == sizeof(Matrix4)); memcpy(result.M, M, sizeof(M)); return result; } void ToString(char* dest, UPInt destsize) const { UPInt pos = 0; for (int r=0; r<4; r++) for (int c=0; c<4; c++) pos += OVR_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]); } static Matrix4 FromString(const char* src) { Matrix4 result; for (int r=0; r<4; r++) for (int c=0; c<4; c++) { result.M[r][c] = (T)atof(src); while (src && *src != ' ') src++; while (src && *src == ' ') src++; } return result; } static const Matrix4& Identity() { return IdentityValue; } void SetIdentity() { M[0][0] = M[1][1] = M[2][2] = M[3][3] = 1; M[0][1] = M[1][0] = M[2][3] = M[3][1] = 0; M[0][2] = M[1][2] = M[2][0] = M[3][2] = 0; M[0][3] = M[1][3] = M[2][1] = M[3][0] = 0; } bool operator== (const Matrix4& b) const { bool isEqual = true; for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) isEqual &= (M[i][j] == b.M[i][j]); return isEqual; } Matrix4 operator+ (const Matrix4& b) const { Matrix4 result(*this); result += b; return result; } Matrix4& operator+= (const Matrix4& b) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) M[i][j] += b.M[i][j]; return *this; } Matrix4 operator- (const Matrix4& b) const { Matrix4 result(*this); result -= b; return result; } Matrix4& operator-= (const Matrix4& b) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) M[i][j] -= b.M[i][j]; return *this; } // Multiplies two matrices into destination with minimum copying. static Matrix4& Multiply(Matrix4* d, const Matrix4& a, const Matrix4& b) { OVR_ASSERT((d != &a) && (d != &b)); int i = 0; do { d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0] + a.M[i][3] * b.M[3][0]; d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1] + a.M[i][3] * b.M[3][1]; d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2] + a.M[i][3] * b.M[3][2]; d->M[i][3] = a.M[i][0] * b.M[0][3] + a.M[i][1] * b.M[1][3] + a.M[i][2] * b.M[2][3] + a.M[i][3] * b.M[3][3]; } while((++i) < 4); return *d; } Matrix4 operator* (const Matrix4& b) const { Matrix4 result(Matrix4::NoInit); Multiply(&result, *this, b); return result; } Matrix4& operator*= (const Matrix4& b) { return Multiply(this, Matrix4(*this), b); } Matrix4 operator* (T s) const { Matrix4 result(*this); result *= s; return result; } Matrix4& operator*= (T s) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) M[i][j] *= s; return *this; } Matrix4 operator/ (T s) const { Matrix4 result(*this); result /= s; return result; } Matrix4& operator/= (T s) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) M[i][j] /= s; return *this; } Vector3 Transform(const Vector3& v) const { return Vector3(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3], M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3], M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]); } Matrix4 Transposed() const { return Matrix4(M[0][0], M[1][0], M[2][0], M[3][0], M[0][1], M[1][1], M[2][1], M[3][1], M[0][2], M[1][2], M[2][2], M[3][2], M[0][3], M[1][3], M[2][3], M[3][3]); } void Transpose() { *this = Transposed(); } T SubDet (const UPInt* rows, const UPInt* cols) const { return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); } T Cofactor(UPInt I, UPInt J) const { const UPInt indices[4][3] = {{1,2,3},{0,2,3},{0,1,3},{0,1,2}}; return ((I+J)&1) ? -SubDet(indices[I],indices[J]) : SubDet(indices[I],indices[J]); } T Determinant() const { return M[0][0] * Cofactor(0,0) + M[0][1] * Cofactor(0,1) + M[0][2] * Cofactor(0,2) + M[0][3] * Cofactor(0,3); } Matrix4 Adjugated() const { return Matrix4(Cofactor(0,0), Cofactor(1,0), Cofactor(2,0), Cofactor(3,0), Cofactor(0,1), Cofactor(1,1), Cofactor(2,1), Cofactor(3,1), Cofactor(0,2), Cofactor(1,2), Cofactor(2,2), Cofactor(3,2), Cofactor(0,3), Cofactor(1,3), Cofactor(2,3), Cofactor(3,3)); } Matrix4 Inverted() const { T det = Determinant(); assert(det != 0); return Adjugated() * (1.0f/det); } void Invert() { *this = Inverted(); } // This is more efficient than general inverse, but ONLY works // correctly if it is a homogeneous transform matrix (rot + trans) Matrix4 InvertedHomogeneousTransform() const { // Make the inverse rotation matrix Matrix4 rinv = this->Transposed(); rinv.M[3][0] = rinv.M[3][1] = rinv.M[3][2] = 0.0f; // Make the inverse translation matrix Vector3 tvinv(-M[0][3],-M[1][3],-M[2][3]); Matrix4 tinv = Matrix4::Translation(tvinv); return rinv * tinv; // "untranslate", then "unrotate" } // This is more efficient than general inverse, but ONLY works // correctly if it is a homogeneous transform matrix (rot + trans) void InvertHomogeneousTransform() { *this = InvertedHomogeneousTransform(); } // Matrix to Euler Angles conversion // a,b,c, are the YawPitchRoll angles to be returned // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A3 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void ToEulerAngles(T *a, T *b, T *c) { OVR_COMPILER_ASSERT((A1 != A2) && (A2 != A3) && (A1 != A3)); T psign = -1; if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation psign = 1; T pm = psign*M[A1][A3]; if (pm < -1.0f + Math::SingularityRadius) { // South pole singularity *a = 0; *b = -S*D*Math::PiOver2; *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); } else if (pm > 1.0f - Math::SingularityRadius) { // North pole singularity *a = 0; *b = S*D*Math::PiOver2; *c = S*D*atan2( psign*M[A2][A1], M[A2][A2] ); } else { // Normal case (nonsingular) *a = S*D*atan2( -psign*M[A2][A3], M[A3][A3] ); *b = S*D*asin(pm); *c = S*D*atan2( -psign*M[A1][A2], M[A1][A1] ); } return; } // Matrix to Euler Angles conversion // a,b,c, are the YawPitchRoll angles to be returned // rotation a around axis A1 // is followed by rotation b around axis A2 // is followed by rotation c around axis A1 // rotations are CCW or CW (D) in LH or RH coordinate system (S) template void ToEulerAnglesABA(T *a, T *b, T *c) { OVR_COMPILER_ASSERT(A1 != A2); // Determine the axis that was not supplied int m = 3 - A1 - A2; T psign = -1; if ((A1 + 1) % 3 == A2) // Determine whether even permutation psign = 1.0f; T c2 = M[A1][A1]; if (c2 < -1 + Math::SingularityRadius) { // South pole singularity *a = 0; *b = S*D*Math::Pi; *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); } else if (c2 > 1.0f - Math::SingularityRadius) { // North pole singularity *a = 0; *b = 0; *c = S*D*atan2( -psign*M[A2][m],M[A2][A2]); } else { // Normal case (nonsingular) *a = S*D*atan2( M[A2][A1],-psign*M[m][A1]); *b = S*D*acos(c2); *c = S*D*atan2( M[A1][A2],psign*M[A1][m]); } return; } // Creates a matrix that converts the vertices from one coordinate system // to another. static Matrix4 AxisConversion(const WorldAxes& to, const WorldAxes& from) { // Holds axis values from the 'to' structure int toArray[3] = { to.XAxis, to.YAxis, to.ZAxis }; // The inverse of the toArray int inv[4]; inv[0] = inv[abs(to.XAxis)] = 0; inv[abs(to.YAxis)] = 1; inv[abs(to.ZAxis)] = 2; Matrix4 m(0, 0, 0, 0, 0, 0, 0, 0, 0); // Only three values in the matrix need to be changed to 1 or -1. m.M[inv[abs(from.XAxis)]][0] = T(from.XAxis/toArray[inv[abs(from.XAxis)]]); m.M[inv[abs(from.YAxis)]][1] = T(from.YAxis/toArray[inv[abs(from.YAxis)]]); m.M[inv[abs(from.ZAxis)]][2] = T(from.ZAxis/toArray[inv[abs(from.ZAxis)]]); return m; } // Creates a matrix for translation by vector static Matrix4 Translation(const Vector3& v) { Matrix4 t; t.M[0][3] = v.x; t.M[1][3] = v.y; t.M[2][3] = v.z; return t; } // Creates a matrix for translation by vector static Matrix4 Translation(T x, T y, T z = 0.0f) { Matrix4 t; t.M[0][3] = x; t.M[1][3] = y; t.M[2][3] = z; return t; } // Sets the translation part void SetTranslation(const Vector3& v) { M[0][3] = v.x; M[1][3] = v.y; M[2][3] = v.z; } Vector3 GetTranslation() const { return Vector3( M[0][3], M[1][3], M[2][3] ); } // Creates a matrix for scaling by vector static Matrix4 Scaling(const Vector3& v) { Matrix4 t; t.M[0][0] = v.x; t.M[1][1] = v.y; t.M[2][2] = v.z; return t; } // Creates a matrix for scaling by vector static Matrix4 Scaling(T x, T y, T z) { Matrix4 t; t.M[0][0] = x; t.M[1][1] = y; t.M[2][2] = z; return t; } // Creates a matrix for scaling by constant static Matrix4 Scaling(T s) { Matrix4 t; t.M[0][0] = s; t.M[1][1] = s; t.M[2][2] = s; return t; } // Simple L1 distance in R^12 T Distance(const Matrix4& m2) const { T d = fabs(M[0][0] - m2.M[0][0]) + fabs(M[0][1] - m2.M[0][1]); d += fabs(M[0][2] - m2.M[0][2]) + fabs(M[0][3] - m2.M[0][3]); d += fabs(M[1][0] - m2.M[1][0]) + fabs(M[1][1] - m2.M[1][1]); d += fabs(M[1][2] - m2.M[1][2]) + fabs(M[1][3] - m2.M[1][3]); d += fabs(M[2][0] - m2.M[2][0]) + fabs(M[2][1] - m2.M[2][1]); d += fabs(M[2][2] - m2.M[2][2]) + fabs(M[2][3] - m2.M[2][3]); d += fabs(M[3][0] - m2.M[3][0]) + fabs(M[3][1] - m2.M[3][1]); d += fabs(M[3][2] - m2.M[3][2]) + fabs(M[3][3] - m2.M[3][3]); return d; } // Creates a rotation matrix rotating around the X axis by 'angle' radians. // Just for quick testing. Not for final API. Need to remove case. static Matrix4 RotationAxis(Axis A, T angle, RotateDirection d, HandedSystem s) { T sina = s * d *sin(angle); T cosa = cos(angle); switch(A) { case Axis_X: return Matrix4(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); case Axis_Y: return Matrix4(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); case Axis_Z: return Matrix4(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); } } // Creates a rotation matrix rotating around the X axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4 RotationX(T angle) { T sina = sin(angle); T cosa = cos(angle); return Matrix4(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); } // Creates a rotation matrix rotating around the Y axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4 RotationY(T angle) { T sina = sin(angle); T cosa = cos(angle); return Matrix4(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); } // Creates a rotation matrix rotating around the Z axis by 'angle' radians. // Rotation direction is depends on the coordinate system: // RHS (Oculus default): Positive angle values rotate Counter-clockwise (CCW), // while looking in the negative axis direction. This is the // same as looking down from positive axis values towards origin. // LHS: Positive angle values rotate clock-wise (CW), while looking in the // negative axis direction. static Matrix4 RotationZ(T angle) { T sina = sin(angle); T cosa = cos(angle); return Matrix4(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); } // LookAtRH creates a View transformation matrix for right-handed coordinate system. // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' // specifying the up vector. The resulting matrix should be used with PerspectiveRH // projection. static Matrix4 LookAtRH(const Vector3& eye, const Vector3& at, const Vector3& up) { Vector3 z = (eye - at).Normalized(); // Forward Vector3 x = up.Cross(z).Normalized(); // Right Vector3 y = z.Cross(x); Matrix4 m(x.x, x.y, x.z, -(x.Dot(eye)), y.x, y.y, y.z, -(y.Dot(eye)), z.x, z.y, z.z, -(z.Dot(eye)), 0, 0, 0, 1 ); return m; } // LookAtLH creates a View transformation matrix for left-handed coordinate system. // The resulting matrix points camera from 'eye' towards 'at' direction, with 'up' // specifying the up vector. static Matrix4 LookAtLH(const Vector3& eye, const Vector3& at, const Vector3& up) { Vector3 z = (at - eye).Normalized(); // Forward Vector3 x = up.Cross(z).Normalized(); // Right Vector3 y = z.Cross(x); Matrix4 m(x.x, x.y, x.z, -(x.Dot(eye)), y.x, y.y, y.z, -(y.Dot(eye)), z.x, z.y, z.z, -(z.Dot(eye)), 0, 0, 0, 1 ); return m; } // PerspectiveRH creates a right-handed perspective projection matrix that can be // used with the Oculus sample renderer. // yfov - Specifies vertical field of view in radians. // aspect - Screen aspect ration, which is usually width/height for square pixels. // Note that xfov = yfov * aspect. // znear - Absolute value of near Z clipping clipping range. // zfar - Absolute value of far Z clipping clipping range (larger then near). // Even though RHS usually looks in the direction of negative Z, positive values // are expected for znear and zfar. static Matrix4 PerspectiveRH(T yfov, T aspect, T znear, T zfar) { Matrix4 m; T tanHalfFov = tan(yfov * 0.5f); m.M[0][0] = 1 / (aspect * tanHalfFov); m.M[1][1] = 1 / tanHalfFov; m.M[2][2] = zfar / (zfar - znear); m.M[3][2] = 1; m.M[2][3] = (zfar * znear) / (znear - zfar); m.M[3][3] = 0; // Note: Post-projection matrix result assumes Left-Handed coordinate system, // with Y up, X right and Z forward. This supports positive z-buffer values. return m; } // PerspectiveRH creates a left-handed perspective projection matrix that can be // used with the Oculus sample renderer. // yfov - Specifies vertical field of view in radians. // aspect - Screen aspect ration, which is usually width/height for square pixels. // Note that xfov = yfov * aspect. // znear - Absolute value of near Z clipping clipping range. // zfar - Absolute value of far Z clipping clipping range (larger then near). static Matrix4 PerspectiveLH(T yfov, T aspect, T znear, T zfar) { Matrix4 m; T tanHalfFov = tan(yfov * 0.5f); m.M[0][0] = 1.0 / (aspect * tanHalfFov); m.M[1][1] = 1.0 / tanHalfFov; m.M[2][2] = zfar / (znear - zfar); // m.M[2][2] = zfar / (zfar - znear); m.M[3][2] = -1.0; m.M[2][3] = (zfar * znear) / (znear - zfar); m.M[3][3] = 0.0; // Note: Post-projection matrix result assumes Left-Handed coordinate system, // with Y up, X right and Z forward. This supports positive z-buffer values. // This is the case even for RHS cooridnate input. return m; } static Matrix4 Ortho2D(T w, T h) { Matrix4 m; m.M[0][0] = 2.0/w; m.M[1][1] = -2.0/h; m.M[0][3] = -1.0; m.M[1][3] = 1.0; m.M[2][2] = 0; return m; } }; typedef Matrix4 Matrix4f; typedef Matrix4 Matrix4d; //------------------------------------------------------------------------------------- // ***** Matrix3 // // Matrix3 is a 3x3 matrix used for representing a rotation matrix. // The matrix is stored in row-major order in memory, meaning that values // of the first row are stored before the next one. // // The arrangement of the matrix is chosen to be in Right-Handed // coordinate system and counterclockwise rotations when looking down // the axis // // Transformation Order: // - Transformations are applied from right to left, so the expression // M1 * M2 * M3 * V means that the vector V is transformed by M3 first, // followed by M2 and M1. // // Coordinate system: Right Handed // // Rotations: Counterclockwise when looking down the axis. All angles are in radians. template class SymMat3; template class Matrix3 { static const Matrix3 IdentityValue; public: T M[3][3]; enum NoInitType { NoInit }; // Construct with no memory initialization. Matrix3(NoInitType) { } // By default, we construct identity matrix. Matrix3() { SetIdentity(); } Matrix3(T m11, T m12, T m13, T m21, T m22, T m23, T m31, T m32, T m33) { M[0][0] = m11; M[0][1] = m12; M[0][2] = m13; M[1][0] = m21; M[1][1] = m22; M[1][2] = m23; M[2][0] = m31; M[2][1] = m32; M[2][2] = m33; } /* explicit Matrix3(const Quat& q) { T ww = q.w*q.w; T xx = q.x*q.x; T yy = q.y*q.y; T zz = q.z*q.z; M[0][0] = ww + xx - yy - zz; M[0][1] = 2 * (q.x*q.y - q.w*q.z); M[0][2] = 2 * (q.x*q.z + q.w*q.y); M[1][0] = 2 * (q.x*q.y + q.w*q.z); M[1][1] = ww - xx + yy - zz; M[1][2] = 2 * (q.y*q.z - q.w*q.x); M[2][0] = 2 * (q.x*q.z - q.w*q.y); M[2][1] = 2 * (q.y*q.z + q.w*q.x); M[2][2] = ww - xx - yy + zz; } */ explicit Matrix3(const Quat& q) { const T tx = q.x+q.x, ty = q.y+q.y, tz = q.z+q.z; const T twx = q.w*tx, twy = q.w*ty, twz = q.w*tz; const T txx = q.x*tx, txy = q.x*ty, txz = q.x*tz; const T tyy = q.y*ty, tyz = q.y*tz, tzz = q.z*tz; M[0][0] = T(1) - (tyy + tzz); M[0][1] = txy - twz; M[0][2] = txz + twy; M[1][0] = txy + twz; M[1][1] = T(1) - (txx + tzz); M[1][2] = tyz - twx; M[2][0] = txz - twy; M[2][1] = tyz + twx; M[2][2] = T(1) - (txx + tyy); } inline explicit Matrix3(T s) { M[0][0] = M[1][1] = M[2][2] = s; M[0][1] = M[0][2] = M[1][0] = M[1][2] = M[2][0] = M[2][1] = 0; } explicit Matrix3(const Transform& p) { Matrix3 result(p.Rotation); result.SetTranslation(p.Translation); *this = result; } // C-interop support explicit Matrix3(const Matrix4::OtherFloatType> &src) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] = (T)src.M[i][j]; } // C-interop support. Matrix3(const typename CompatibleTypes >::Type& s) { OVR_COMPILER_ASSERT(sizeof(s) == sizeof(Matrix3)); memcpy(M, s.M, sizeof(M)); } operator typename CompatibleTypes >::Type () const { typename CompatibleTypes >::Type result; OVR_COMPILER_ASSERT(sizeof(result) == sizeof(Matrix3)); memcpy(result.M, M, sizeof(M)); return result; } void ToString(char* dest, UPInt destsize) const { UPInt pos = 0; for (int r=0; r<3; r++) for (int c=0; c<3; c++) pos += OVR_sprintf(dest+pos, destsize-pos, "%g ", M[r][c]); } static Matrix3 FromString(const char* src) { Matrix3 result; for (int r=0; r<3; r++) for (int c=0; c<3; c++) { result.M[r][c] = (T)atof(src); while (src && *src != ' ') src++; while (src && *src == ' ') src++; } return result; } static const Matrix3& Identity() { return IdentityValue; } void SetIdentity() { M[0][0] = M[1][1] = M[2][2] = 1; M[0][1] = M[1][0] = M[2][0] = 0; M[0][2] = M[1][2] = M[2][1] = 0; } bool operator== (const Matrix3& b) const { bool isEqual = true; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) isEqual &= (M[i][j] == b.M[i][j]); return isEqual; } Matrix3 operator+ (const Matrix3& b) const { Matrix4 result(*this); result += b; return result; } Matrix3& operator+= (const Matrix3& b) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] += b.M[i][j]; return *this; } void operator= (const Matrix3& b) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] = b.M[i][j]; return; } void operator= (const SymMat3& b) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] = 0; M[0][0] = b.v[0]; M[0][1] = b.v[1]; M[0][2] = b.v[2]; M[1][1] = b.v[3]; M[1][2] = b.v[4]; M[2][2] = b.v[5]; return; } Matrix3 operator- (const Matrix3& b) const { Matrix3 result(*this); result -= b; return result; } Matrix3& operator-= (const Matrix3& b) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] -= b.M[i][j]; return *this; } // Multiplies two matrices into destination with minimum copying. static Matrix3& Multiply(Matrix3* d, const Matrix3& a, const Matrix3& b) { OVR_ASSERT((d != &a) && (d != &b)); int i = 0; do { d->M[i][0] = a.M[i][0] * b.M[0][0] + a.M[i][1] * b.M[1][0] + a.M[i][2] * b.M[2][0]; d->M[i][1] = a.M[i][0] * b.M[0][1] + a.M[i][1] * b.M[1][1] + a.M[i][2] * b.M[2][1]; d->M[i][2] = a.M[i][0] * b.M[0][2] + a.M[i][1] * b.M[1][2] + a.M[i][2] * b.M[2][2]; } while((++i) < 3); return *d; } Matrix3 operator* (const Matrix3& b) const { Matrix3 result(Matrix3::NoInit); Multiply(&result, *this, b); return result; } Matrix3& operator*= (const Matrix3& b) { return Multiply(this, Matrix3(*this), b); } Matrix3 operator* (T s) const { Matrix3 result(*this); result *= s; return result; } Matrix3& operator*= (T s) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] *= s; return *this; } Vector3 operator* (const Vector3 &b) const { Vector3 result; result.x = M[0][0]*b.x + M[0][1]*b.y + M[0][2]*b.z; result.y = M[1][0]*b.x + M[1][1]*b.y + M[1][2]*b.z; result.z = M[2][0]*b.x + M[2][1]*b.y + M[2][2]*b.z; return result; } Matrix3 operator/ (T s) const { Matrix3 result(*this); result /= s; return result; } Matrix3& operator/= (T s) { for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) M[i][j] /= s; return *this; } Vector3 Transform(const Vector3& v) const { return Vector3(M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z, M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z, M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z); } Matrix3 Transposed() const { return Matrix3(M[0][0], M[1][0], M[2][0], M[0][1], M[1][1], M[2][1], M[0][2], M[1][2], M[2][2]); } void Transpose() { *this = Transposed(); } T SubDet (const UPInt* rows, const UPInt* cols) const { return M[rows[0]][cols[0]] * (M[rows[1]][cols[1]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[1]]) - M[rows[0]][cols[1]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[2]] - M[rows[1]][cols[2]] * M[rows[2]][cols[0]]) + M[rows[0]][cols[2]] * (M[rows[1]][cols[0]] * M[rows[2]][cols[1]] - M[rows[1]][cols[1]] * M[rows[2]][cols[0]]); } // M += a*b.t() inline void Rank1Add(const Vector3 &a, const Vector3 &b) { M[0][0] += a.x*b.x; M[0][1] += a.x*b.y; M[0][2] += a.x*b.z; M[1][0] += a.y*b.x; M[1][1] += a.y*b.y; M[1][2] += a.y*b.z; M[2][0] += a.z*b.x; M[2][1] += a.z*b.y; M[2][2] += a.z*b.z; } // M -= a*b.t() inline void Rank1Sub(const Vector3 &a, const Vector3 &b) { M[0][0] -= a.x*b.x; M[0][1] -= a.x*b.y; M[0][2] -= a.x*b.z; M[1][0] -= a.y*b.x; M[1][1] -= a.y*b.y; M[1][2] -= a.y*b.z; M[2][0] -= a.z*b.x; M[2][1] -= a.z*b.y; M[2][2] -= a.z*b.z; } inline Vector3 Col(int c) const { return Vector3(M[0][c], M[1][c], M[2][c]); } inline Vector3 Row(int r) const { return Vector3(M[r][0], M[r][1], M[r][2]); } inline T Determinant() const { const Matrix3& m = *this; T d; d = m.M[0][0] * (m.M[1][1]*m.M[2][2] - m.M[1][2] * m.M[2][1]); d -= m.M[0][1] * (m.M[1][0]*m.M[2][2] - m.M[1][2] * m.M[2][0]); d += m.M[0][2] * (m.M[1][0]*m.M[2][1] - m.M[1][1] * m.M[2][0]); return d; } inline Matrix3 Inverse() const { Matrix3 a; const Matrix3& m = *this; T d = Determinant(); assert(d != 0); T s = T(1)/d; a.M[0][0] = s * (m.M[1][1] * m.M[2][2] - m.M[1][2] * m.M[2][1]); a.M[1][0] = s * (m.M[1][2] * m.M[2][0] - m.M[1][0] * m.M[2][2]); a.M[2][0] = s * (m.M[1][0] * m.M[2][1] - m.M[1][1] * m.M[2][0]); a.M[0][1] = s * (m.M[0][2] * m.M[2][1] - m.M[0][1] * m.M[2][2]); a.M[1][1] = s * (m.M[0][0] * m.M[2][2] - m.M[0][2] * m.M[2][0]); a.M[2][1] = s * (m.M[0][1] * m.M[2][0] - m.M[0][0] * m.M[2][1]); a.M[0][2] = s * (m.M[0][1] * m.M[1][2] - m.M[0][2] * m.M[1][1]); a.M[1][2] = s * (m.M[0][2] * m.M[1][0] - m.M[0][0] * m.M[1][2]); a.M[2][2] = s * (m.M[0][0] * m.M[1][1] - m.M[0][1] * m.M[1][0]); return a; } }; typedef Matrix3 Matrix3f; typedef Matrix3 Matrix3d; //------------------------------------------------------------------------------------- template class SymMat3 { private: typedef SymMat3 this_type; public: typedef T Value_t; // Upper symmetric T v[6]; // _00 _01 _02 _11 _12 _22 inline SymMat3() {} inline explicit SymMat3(T s) { v[0] = v[3] = v[5] = s; v[1] = v[2] = v[4] = 0; } inline explicit SymMat3(T a00, T a01, T a02, T a11, T a12, T a22) { v[0] = a00; v[1] = a01; v[2] = a02; v[3] = a11; v[4] = a12; v[5] = a22; } static inline int Index(unsigned int i, unsigned int j) { return (i <= j) ? (3*i - i*(i+1)/2 + j) : (3*j - j*(j+1)/2 + i); } inline T operator()(int i, int j) const { return v[Index(i,j)]; } inline T &operator()(int i, int j) { return v[Index(i,j)]; } template inline SymMat3 CastTo() const { return SymMat3(static_cast(v[0]), static_cast(v[1]), static_cast(v[2]), static_cast(v[3]), static_cast(v[4]), static_cast(v[5])); } inline this_type& operator+=(const this_type& b) { v[0]+=b.v[0]; v[1]+=b.v[1]; v[2]+=b.v[2]; v[3]+=b.v[3]; v[4]+=b.v[4]; v[5]+=b.v[5]; return *this; } inline this_type& operator-=(const this_type& b) { v[0]-=b.v[0]; v[1]-=b.v[1]; v[2]-=b.v[2]; v[3]-=b.v[3]; v[4]-=b.v[4]; v[5]-=b.v[5]; return *this; } inline this_type& operator*=(T s) { v[0]*=s; v[1]*=s; v[2]*=s; v[3]*=s; v[4]*=s; v[5]*=s; return *this; } inline SymMat3 operator*(T s) const { SymMat3 d; d.v[0] = v[0]*s; d.v[1] = v[1]*s; d.v[2] = v[2]*s; d.v[3] = v[3]*s; d.v[4] = v[4]*s; d.v[5] = v[5]*s; return d; } // Multiplies two matrices into destination with minimum copying. static SymMat3& Multiply(SymMat3* d, const SymMat3& a, const SymMat3& b) { // _00 _01 _02 _11 _12 _22 d->v[0] = a.v[0] * b.v[0]; d->v[1] = a.v[0] * b.v[1] + a.v[1] * b.v[3]; d->v[2] = a.v[0] * b.v[2] + a.v[1] * b.v[4]; d->v[3] = a.v[3] * b.v[3]; d->v[4] = a.v[3] * b.v[4] + a.v[4] * b.v[5]; d->v[5] = a.v[5] * b.v[5]; return *d; } inline T Determinant() const { const this_type& m = *this; T d; d = m(0,0) * (m(1,1)*m(2,2) - m(1,2) * m(2,1)); d -= m(0,1) * (m(1,0)*m(2,2) - m(1,2) * m(2,0)); d += m(0,2) * (m(1,0)*m(2,1) - m(1,1) * m(2,0)); return d; } inline this_type Inverse() const { this_type a; const this_type& m = *this; T d = Determinant(); assert(d != 0); T s = T(1)/d; a(0,0) = s * (m(1,1) * m(2,2) - m(1,2) * m(2,1)); a(0,1) = s * (m(0,2) * m(2,1) - m(0,1) * m(2,2)); a(1,1) = s * (m(0,0) * m(2,2) - m(0,2) * m(2,0)); a(0,2) = s * (m(0,1) * m(1,2) - m(0,2) * m(1,1)); a(1,2) = s * (m(0,2) * m(1,0) - m(0,0) * m(1,2)); a(2,2) = s * (m(0,0) * m(1,1) - m(0,1) * m(1,0)); return a; } inline T Trace() const { return v[0] + v[3] + v[5]; } // M = a*a.t() inline void Rank1(const Vector3 &a) { v[0] = a.x*a.x; v[1] = a.x*a.y; v[2] = a.x*a.z; v[3] = a.y*a.y; v[4] = a.y*a.z; v[5] = a.z*a.z; } // M += a*a.t() inline void Rank1Add(const Vector3 &a) { v[0] += a.x*a.x; v[1] += a.x*a.y; v[2] += a.x*a.z; v[3] += a.y*a.y; v[4] += a.y*a.z; v[5] += a.z*a.z; } // M -= a*a.t() inline void Rank1Sub(const Vector3 &a) { v[0] -= a.x*a.x; v[1] -= a.x*a.y; v[2] -= a.x*a.z; v[3] -= a.y*a.y; v[4] -= a.y*a.z; v[5] -= a.z*a.z; } }; typedef SymMat3 SymMat3f; typedef SymMat3 SymMat3d; template inline Matrix3 operator*(const SymMat3& a, const SymMat3& b) { #define AJB_ARBC(r,c) (a(r,0)*b(0,c)+a(r,1)*b(1,c)+a(r,2)*b(2,c)) return Matrix3( AJB_ARBC(0,0), AJB_ARBC(0,1), AJB_ARBC(0,2), AJB_ARBC(1,0), AJB_ARBC(1,1), AJB_ARBC(1,2), AJB_ARBC(2,0), AJB_ARBC(2,1), AJB_ARBC(2,2)); #undef AJB_ARBC } template inline Matrix3 operator*(const Matrix3& a, const SymMat3& b) { #define AJB_ARBC(r,c) (a(r,0)*b(0,c)+a(r,1)*b(1,c)+a(r,2)*b(2,c)) return Matrix3( AJB_ARBC(0,0), AJB_ARBC(0,1), AJB_ARBC(0,2), AJB_ARBC(1,0), AJB_ARBC(1,1), AJB_ARBC(1,2), AJB_ARBC(2,0), AJB_ARBC(2,1), AJB_ARBC(2,2)); #undef AJB_ARBC } //------------------------------------------------------------------------------------- // ***** Angle // Cleanly representing the algebra of 2D rotations. // The operations maintain the angle between -Pi and Pi, the same range as atan2. template class Angle { public: enum AngularUnits { Radians = 0, Degrees = 1 }; Angle() : a(0) {} // Fix the range to be between -Pi and Pi Angle(T a_, AngularUnits u = Radians) : a((u == Radians) ? a_ : a_*Math::DegreeToRadFactor) { FixRange(); } T Get(AngularUnits u = Radians) const { return (u == Radians) ? a : a*Math::RadToDegreeFactor; } void Set(const T& x, AngularUnits u = Radians) { a = (u == Radians) ? x : x*Math::DegreeToRadFactor; FixRange(); } int Sign() const { if (a == 0) return 0; else return (a > 0) ? 1 : -1; } T Abs() const { return (a > 0) ? a : -a; } bool operator== (const Angle& b) const { return a == b.a; } bool operator!= (const Angle& b) const { return a != b.a; } // bool operator< (const Angle& b) const { return a < a.b; } // bool operator> (const Angle& b) const { return a > a.b; } // bool operator<= (const Angle& b) const { return a <= a.b; } // bool operator>= (const Angle& b) const { return a >= a.b; } // bool operator= (const T& x) { a = x; FixRange(); } // These operations assume a is already between -Pi and Pi. Angle& operator+= (const Angle& b) { a = a + b.a; FastFixRange(); return *this; } Angle& operator+= (const T& x) { a = a + x; FixRange(); return *this; } Angle operator+ (const Angle& b) const { Angle res = *this; res += b; return res; } Angle operator+ (const T& x) const { Angle res = *this; res += x; return res; } Angle& operator-= (const Angle& b) { a = a - b.a; FastFixRange(); return *this; } Angle& operator-= (const T& x) { a = a - x; FixRange(); return *this; } Angle operator- (const Angle& b) const { Angle res = *this; res -= b; return res; } Angle operator- (const T& x) const { Angle res = *this; res -= x; return res; } T Distance(const Angle& b) { T c = fabs(a - b.a); return (c <= Math::Pi) ? c : Math::TwoPi - c; } private: // The stored angle, which should be maintained between -Pi and Pi T a; // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side inline void FastFixRange() { if (a < -Math::Pi) a += Math::TwoPi; else if (a > Math::Pi) a -= Math::TwoPi; } // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method inline void FixRange() { // do nothing if the value is already in the correct range, since fmod call is expensive if (a >= -Math::Pi && a <= Math::Pi) return; a = fmod(a,Math::TwoPi); if (a < -Math::Pi) a += Math::TwoPi; else if (a > Math::Pi) a -= Math::TwoPi; } }; typedef Angle Anglef; typedef Angle Angled; //------------------------------------------------------------------------------------- // ***** Plane // Consists of a normal vector and distance from the origin where the plane is located. template class Plane : public RefCountBase > { public: Vector3 N; T D; Plane() : D(0) {} // Normals must already be normalized Plane(const Vector3& n, T d) : N(n), D(d) {} Plane(T x, T y, T z, T d) : N(x,y,z), D(d) {} // construct from a point on the plane and the normal Plane(const Vector3& p, const Vector3& n) : N(n), D(-(p * n)) {} // Find the point to plane distance. The sign indicates what side of the plane the point is on (0 = point on plane). T TestSide(const Vector3& p) const { return (N.Dot(p)) + D; } Plane Flipped() const { return Plane(-N, -D); } void Flip() { N = -N; D = -D; } bool operator==(const Plane& rhs) const { return (this->D == rhs.D && this->N == rhs.N); } }; typedef Plane Planef; } // Namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_RefCount.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_RefCount.cpp new file mode 100644 index 0000000..bd0b96a --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_RefCount.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_RefCount.cpp Content : Reference counting implementation Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_RefCount.h" #include "OVR_Atomic.h" #include "OVR_Log.h" namespace OVR { #ifdef OVR_CC_ARM void* ReturnArg0(void* p) { return p; } #endif // ***** Reference Count Base implementation RefCountImplCore::~RefCountImplCore() { // RefCount can be either 1 or 0 here. // 0 if Release() was properly called. // 1 if the object was declared on stack or as an aggregate. OVR_ASSERT(RefCount <= 1); } #ifdef OVR_BUILD_DEBUG void RefCountImplCore::reportInvalidDelete(void *pmem) { OVR_DEBUG_LOG( ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); OVR_ASSERT(0); } #endif RefCountNTSImplCore::~RefCountNTSImplCore() { // RefCount can be either 1 or 0 here. // 0 if Release() was properly called. // 1 if the object was declared on stack or as an aggregate. OVR_ASSERT(RefCount <= 1); } #ifdef OVR_BUILD_DEBUG void RefCountNTSImplCore::reportInvalidDelete(void *pmem) { OVR_DEBUG_LOG( ("Invalid delete call on ref-counted object at %p. Please use Release()", pmem)); OVR_ASSERT(0); } #endif // *** Thread-Safe RefCountImpl void RefCountImpl::AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } void RefCountImpl::Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) delete this; } // *** Thread-Safe RefCountVImpl w/virtual AddRef/Release void RefCountVImpl::AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } void RefCountVImpl::Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) delete this; } // *** NON-Thread-Safe RefCountImpl void RefCountNTSImpl::Release() const { RefCount--; if (RefCount == 0) delete this; } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_RefCount.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_RefCount.h new file mode 100644 index 0000000..e355c44 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_RefCount.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: Kernel Filename : OVR_RefCount.h Content : Reference counting implementation headers Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_RefCount_h #define OVR_RefCount_h #include "OVR_Types.h" #include "OVR_Allocator.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** Reference Counting // There are three types of reference counting base classes: // // RefCountBase - Provides thread-safe reference counting (Default). // RefCountBaseNTS - Non Thread Safe version of reference counting. // ***** Declared classes template class RefCountBase; template class RefCountBaseNTS; class RefCountImpl; class RefCountNTSImpl; //----------------------------------------------------------------------------------- // ***** Implementation For Reference Counting // RefCountImplCore holds RefCount value and defines a few utility // functions shared by all implementations. class RefCountImplCore { protected: volatile int RefCount; public: // RefCountImpl constructor always initializes RefCount to 1 by default. OVR_FORCE_INLINE RefCountImplCore() : RefCount(1) { } // Need virtual destructor // This: 1. Makes sure the right destructor's called. // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() virtual ~RefCountImplCore(); // Debug method only. int GetRefCount() const { return RefCount; } // This logic is used to detect invalid 'delete' calls of reference counted // objects. Direct delete calls are not allowed on them unless they come in // internally from Release. #ifdef OVR_BUILD_DEBUG static void OVR_CDECL reportInvalidDelete(void *pmem); inline static void checkInvalidDelete(RefCountImplCore *pmem) { if (pmem->RefCount != 0) reportInvalidDelete(pmem); } #else inline static void checkInvalidDelete(RefCountImplCore *) { } #endif // Base class ref-count content should not be copied. void operator = (const RefCountImplCore &) { } }; class RefCountNTSImplCore { protected: mutable int RefCount; public: // RefCountImpl constructor always initializes RefCount to 1 by default. OVR_FORCE_INLINE RefCountNTSImplCore() : RefCount(1) { } // Need virtual destructor // This: 1. Makes sure the right destructor's called. // 2. Makes us have VTable, necessary if we are going to have format needed by InitNewMem() virtual ~RefCountNTSImplCore(); // Debug method only. int GetRefCount() const { return RefCount; } // This logic is used to detect invalid 'delete' calls of reference counted // objects. Direct delete calls are not allowed on them unless they come in // internally from Release. #ifdef OVR_BUILD_DEBUG static void OVR_CDECL reportInvalidDelete(void *pmem); OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *pmem) { if (pmem->RefCount != 0) reportInvalidDelete(pmem); } #else OVR_FORCE_INLINE static void checkInvalidDelete(RefCountNTSImplCore *) { } #endif // Base class ref-count content should not be copied. void operator = (const RefCountNTSImplCore &) { } }; // RefCountImpl provides Thread-Safe implementation of reference counting, so // it should be used by default in most places. class RefCountImpl : public RefCountImplCore { public: // Thread-Safe Ref-Count Implementation. void AddRef(); void Release(); }; // RefCountVImpl provides Thread-Safe implementation of reference counting, plus, // virtual AddRef and Release. class RefCountVImpl : virtual public RefCountImplCore { public: // Thread-Safe Ref-Count Implementation. virtual void AddRef(); virtual void Release(); }; // RefCountImplNTS provides Non-Thread-Safe implementation of reference counting, // which is slightly more efficient since it doesn't use atomics. class RefCountNTSImpl : public RefCountNTSImplCore { public: OVR_FORCE_INLINE void AddRef() const { RefCount++; } void Release() const; }; // RefCountBaseStatImpl<> is a common class that adds new/delete override with Stat tracking // to the reference counting implementation. Base must be one of the RefCountImpl classes. template class RefCountBaseStatImpl : public Base { public: RefCountBaseStatImpl() { } // *** Override New and Delete // DOM-IGNORE-BEGIN // Undef new temporarily if it is being redefined #ifdef OVR_DEFINE_NEW #undef new #endif #ifdef OVR_BUILD_DEBUG // Custom check used to detect incorrect calls of 'delete' on ref-counted objects. #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) \ do {if (p) Base::checkInvalidDelete((class_name*)p); } while(0) #else #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) #endif // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) #undef OVR_REFCOUNTALLOC_CHECK_DELETE #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif // OVR_BUILD_DEFINE_NEW // DOM-IGNORE-END }; template class RefCountBaseStatVImpl : virtual public Base { public: RefCountBaseStatVImpl() { } // *** Override New and Delete // DOM-IGNORE-BEGIN // Undef new temporarily if it is being redefined #ifdef OVR_DEFINE_NEW #undef new #endif #define OVR_REFCOUNTALLOC_CHECK_DELETE(class_name, p) // Redefine all new & delete operators. OVR_MEMORY_REDEFINE_NEW_IMPL(Base, OVR_REFCOUNTALLOC_CHECK_DELETE) #undef OVR_REFCOUNTALLOC_CHECK_DELETE #ifdef OVR_DEFINE_NEW #define new OVR_DEFINE_NEW #endif // OVR_BUILD_DEFINE_NEW // DOM-IGNORE-END }; //----------------------------------------------------------------------------------- // *** End user RefCountBase<> classes // RefCountBase is a base class for classes that require thread-safe reference // counting; it also overrides the new and delete operators to use MemoryHeap. // // Reference counted objects start out with RefCount value of 1. Further lifetime // management is done through the AddRef() and Release() methods, typically // hidden by Ptr<>. template class RefCountBase : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBase() : RefCountBaseStatImpl() { } }; // RefCountBaseV is the same as RefCountBase but with virtual AddRef/Release template class RefCountBaseV : virtual public RefCountBaseStatVImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBaseV() : RefCountBaseStatVImpl() { } }; // RefCountBaseNTS is a base class for classes that require Non-Thread-Safe reference // counting; it also overrides the new and delete operators to use MemoryHeap. // This class should only be used if all pointers to it are known to be assigned, // destroyed and manipulated within one thread. // // Reference counted objects start out with RefCount value of 1. Further lifetime // management is done through the AddRef() and Release() methods, typically // hidden by Ptr<>. template class RefCountBaseNTS : public RefCountBaseStatImpl { public: // Constructor. OVR_FORCE_INLINE RefCountBaseNTS() : RefCountBaseStatImpl() { } }; //----------------------------------------------------------------------------------- // ***** Pickable template pointer enum PickType { PickValue }; template class Pickable { public: Pickable() : pV(NULL) {} explicit Pickable(T* p) : pV(p) {} Pickable(T* p, PickType) : pV(p) { OVR_ASSERT(pV); if (pV) pV->AddRef(); } template Pickable(const Pickable& other) : pV(other.GetPtr()) {} public: Pickable& operator =(const Pickable& other) { OVR_ASSERT(pV == NULL); pV = other.pV; // Extra check. //other.pV = NULL; return *this; } public: T* GetPtr() const { return pV; } T* operator->() const { return pV; } T& operator*() const { OVR_ASSERT(pV); return *pV; } private: T* pV; }; template OVR_FORCE_INLINE Pickable MakePickable(T* p) { return Pickable(p); } //----------------------------------------------------------------------------------- // ***** Ref-Counted template pointer // Automatically AddRefs and Releases interfaces void* ReturnArg0(void* p); template class Ptr { #ifdef OVR_CC_ARM static C* ReturnArg(void* p) { return (C*)ReturnArg0(p); } #endif protected: C *pObject; public: // Constructors OVR_FORCE_INLINE Ptr() : pObject(0) { } #ifdef OVR_CC_ARM OVR_FORCE_INLINE Ptr(C &robj) : pObject(ReturnArg(&robj)) #else OVR_FORCE_INLINE Ptr(C &robj) : pObject(&robj) #endif { } OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) { // No AddRef() on purpose. } OVR_FORCE_INLINE Ptr(Ptr& other, PickType) : pObject(other.pObject) { other.pObject = NULL; // No AddRef() on purpose. } OVR_FORCE_INLINE Ptr(C *pobj) { if (pobj) pobj->AddRef(); pObject = pobj; } OVR_FORCE_INLINE Ptr(const Ptr &src) { if (src.pObject) src.pObject->AddRef(); pObject = src.pObject; } template OVR_FORCE_INLINE Ptr(Ptr &src) { if (src) src->AddRef(); pObject = src; } template OVR_FORCE_INLINE Ptr(Pickable v) : pObject(v.GetPtr()) { // No AddRef() on purpose. } // Destructor OVR_FORCE_INLINE ~Ptr() { if (pObject) pObject->Release(); } // Compares OVR_FORCE_INLINE bool operator == (const Ptr &other) const { return pObject == other.pObject; } OVR_FORCE_INLINE bool operator != (const Ptr &other) const { return pObject != other.pObject; } OVR_FORCE_INLINE bool operator == (C *pother) const { return pObject == pother; } OVR_FORCE_INLINE bool operator != (C *pother) const { return pObject != pother; } OVR_FORCE_INLINE bool operator < (const Ptr &other) const { return pObject < other.pObject; } // Assignment template OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } // Specialization OVR_FORCE_INLINE const Ptr& operator = (const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } OVR_FORCE_INLINE const Ptr& operator = (C *psrc) { if (psrc) psrc->AddRef(); if (pObject) pObject->Release(); pObject = psrc; return *this; } OVR_FORCE_INLINE const Ptr& operator = (C &src) { if (pObject) pObject->Release(); pObject = &src; return *this; } OVR_FORCE_INLINE Ptr& operator = (Pickable src) { return Pick(src); } template OVR_FORCE_INLINE Ptr& operator = (Pickable src) { return Pick(src); } // Set Assignment template OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } // Specialization OVR_FORCE_INLINE Ptr& SetPtr(const Ptr &src) { if (src) src->AddRef(); if (pObject) pObject->Release(); pObject = src; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(C *psrc) { if (psrc) psrc->AddRef(); if (pObject) pObject->Release(); pObject = psrc; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(C &src) { if (pObject) pObject->Release(); pObject = &src; return *this; } OVR_FORCE_INLINE Ptr& SetPtr(Pickable src) { return Pick(src); } // Nulls ref-counted pointer without decrement OVR_FORCE_INLINE void NullWithoutRelease() { pObject = 0; } // Clears the pointer to the object OVR_FORCE_INLINE void Clear() { if (pObject) pObject->Release(); pObject = 0; } // Obtain pointer reference directly, for D3D interfaces OVR_FORCE_INLINE C*& GetRawRef() { return pObject; } // Access Operators OVR_FORCE_INLINE C* GetPtr() const { return pObject; } OVR_FORCE_INLINE C& operator * () const { return *pObject; } OVR_FORCE_INLINE C* operator -> () const { return pObject; } // Conversion OVR_FORCE_INLINE operator C* () const { return pObject; } // Pickers. // Pick a value. OVR_FORCE_INLINE Ptr& Pick(Ptr& other) { if (&other != this) { if (pObject) pObject->Release(); pObject = other.pObject; other.pObject = 0; } return *this; } OVR_FORCE_INLINE Ptr& Pick(Pickable v) { if (v.GetPtr() != pObject) { if (pObject) pObject->Release(); pObject = v.GetPtr(); } return *this; } template OVR_FORCE_INLINE Ptr& Pick(Pickable v) { if (v.GetPtr() != pObject) { if (pObject) pObject->Release(); pObject = v.GetPtr(); } return *this; } OVR_FORCE_INLINE Ptr& Pick(C* p) { if (p != pObject) { if (pObject) pObject->Release(); pObject = p; } return *this; } }; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Std.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Std.cpp new file mode 100644 index 0000000..0fe50b5 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Std.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Std.cpp Content : Standard C function implementation Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Std.h" #include "OVR_Alg.h" // localeconv() call in OVR_strtod() #include namespace OVR { // Source for functions not available on all platforms is included here. // Case insensitive compare implemented in platform-specific way. int OVR_CDECL OVR_stricmp(const char* a, const char* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_stricmp(a, b); #else return ::stricmp(a, b); #endif #else return strcasecmp(a, b); #endif } int OVR_CDECL OVR_strnicmp(const char* a, const char* b, UPInt count) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_strnicmp(a, b, count); #else return ::strnicmp(a, b, count); #endif #else return strncasecmp(a, b, count); #endif } wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src) { #if defined(OVR_MSVC_SAFESTRING) wcscpy_s(dest, destsize, src); return dest; #elif defined(OVR_OS_WIN32) OVR_UNUSED(destsize); wcscpy(dest, src); return dest; #else UPInt l = OVR_wcslen(src) + 1; // incl term null l = (l < destsize) ? l : destsize; memcpy(dest, src, l * sizeof(wchar_t)); return dest; #endif } wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count) { #if defined(OVR_MSVC_SAFESTRING) wcsncpy_s(dest, destsize, src, count); return dest; #else UPInt srclen = OVR_wcslen(src); UPInt l = Alg::Min(srclen, count); l = (l < destsize) ? l : destsize; memcpy(dest, src, l * sizeof(wchar_t)); if (count > srclen) { UPInt remLen = Alg::Min(destsize - l, (count - srclen)); memset(&dest[l], 0, sizeof(wchar_t)*remLen); } else if (l < destsize) dest[l] = 0; return dest; #endif } wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src) { #if defined(OVR_MSVC_SAFESTRING) wcscat_s(dest, destsize, src); return dest; #elif defined(OVR_OS_WIN32) OVR_UNUSED(destsize); wcscat(dest, src); return dest; #else UPInt dstlen = OVR_wcslen(dest); // do not incl term null UPInt srclen = OVR_wcslen(src) + 1; // incl term null UPInt copylen = (dstlen + srclen < destsize) ? srclen : destsize - dstlen; memcpy(dest + dstlen, src, copylen * sizeof(wchar_t)); return dest; #endif } UPInt OVR_CDECL OVR_wcslen(const wchar_t* str) { #if defined(OVR_OS_WIN32) return wcslen(str); #else UPInt i = 0; while(str[i] != '\0') ++i; return i; #endif } int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) return wcscmp(a, b); #else // not supported, use custom implementation const wchar_t *pa = a, *pb = b; while (*pa && *pb) { wchar_t ca = *pa; wchar_t cb = *pb; if (ca < cb) return -1; else if (ca > cb) return 1; pa++; pb++; } if (*pa) return 1; else if (*pb) return -1; else return 0; #endif } int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_wcsicmp(a, b); #else return ::wcsicmp(a, b); #endif #elif defined(OVR_OS_MAC) || defined(__CYGWIN__) || defined(OVR_OS_ANDROID) || defined(OVR_OS_IPHONE) // not supported, use custom implementation const wchar_t *pa = a, *pb = b; while (*pa && *pb) { wchar_t ca = OVR_towlower(*pa); wchar_t cb = OVR_towlower(*pb); if (ca < cb) return -1; else if (ca > cb) return 1; pa++; pb++; } if (*pa) return 1; else if (*pb) return -1; else return 0; #else return wcscasecmp(a, b); #endif } // This function is not inline because of dependency on double OVR_CDECL OVR_strtod(const char* string, char** tailptr) { #if !defined(OVR_OS_ANDROID) const char s = *localeconv()->decimal_point; if (s != '.') { char buffer[347 + 1]; OVR_strcpy(buffer, sizeof(buffer), string); for (char* c = buffer; *c != '\0'; ++c) { if (*c == '.') { *c = s; break; } } return strtod(buffer, tailptr); } #endif return strtod(string, tailptr); } #ifndef OVR_NO_WCTYPE //// Use this class to generate Unicode bitsets. For example: //// //// UnicodeBitSet bitSet; //// for(unsigned i = 0; i < 65536; ++i) //// { //// if (iswalpha(i)) //// bitSet.Set(i); //// } //// bitSet.Dump(); //// ////--------------------------------------------------------------- //class UnicodeBitSet //{ //public: // UnicodeBitSet() // { // memset(Offsets, 0, sizeof(Offsets)); // memset(Bits, 0, sizeof(Bits)); // } // // void Set(unsigned bit) { Bits[bit >> 8][(bit >> 4) & 15] |= 1 << (bit & 15); } // // void Dump() // { // unsigned i, j; // unsigned offsetCount = 0; // for(i = 0; i < 256; ++i) // { // if (isNull(i)) Offsets[i] = 0; // else // if (isFull(i)) Offsets[i] = 1; // else Offsets[i] = UInt16(offsetCount++ * 16 + 256); // } // for(i = 0; i < 16; ++i) // { // for(j = 0; j < 16; ++j) // { // printf("%5u,", Offsets[i*16+j]); // } // printf("\n"); // } // for(i = 0; i < 256; ++i) // { // if (Offsets[i] > 255) // { // for(j = 0; j < 16; j++) // { // printf("%5u,", Bits[i][j]); // } // printf("\n"); // } // } // } // //private: // bool isNull(unsigned n) const // { // const UInt16* p = Bits[n]; // for(unsigned i = 0; i < 16; ++i) // if (p[i] != 0) return false; // return true; // } // // bool isFull(unsigned n) const // { // const UInt16* p = Bits[n]; // for(unsigned i = 0; i < 16; ++i) // if (p[i] != 0xFFFF) return false; // return true; // } // // UInt16 Offsets[256]; // UInt16 Bits[256][16]; //}; const UInt16 UnicodeAlnumBits[] = { 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, 0, 0, 0, 1023,65534, 2047,65534, 2047, 0, 0, 0, 524,65535,65407,65535,65407, 65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, 65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, 0, 0,65534, 2047,65534, 63, 1023,65535,65535,65535,65535,65535,65535, 8175, 8702, 8191, 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, 65518,65535,65535,58367, 8191,65281,65487, 0,40942,65529,65023,50117, 6559,45184,65487, 3, 34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, 40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, 57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, 57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 12, 65534,65535,65535, 2047,32767, 1023, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, 1, 0, 1023, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, 65535,65535,63227, 327, 1023, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, 65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, 65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, 32767,32573,65535,65535,65407, 2047,65024, 3, 0, 0,65535,65535,65535,65535,65535, 31, 65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 1023, 0, 0, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, 65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, 64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, 65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, 65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, 65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 1023,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; const UInt16 UnicodeAlphaBits[] = { 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, 640, 656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 672, 688, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 704, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, 1, 1, 736, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 768, 784, 1, 800, 816, 832, 0, 0, 0, 0,65534, 2047,65534, 2047, 0, 0, 0, 0,65535,65407,65535,65407, 65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, 0, 0, 0, 0, 32, 0, 0, 1024,55104,65535,65531,65535,32767,64767,65535, 15, 65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, 0, 0, 0,65534,65535, 639,65534,65535, 255, 0, 0, 0, 0,65535, 2047, 7, 0, 0,65534, 2047,65534, 63, 0,65535,65535,65535,65535,65535,65535, 8175, 8702, 7168, 0,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, 65518,65535,65535,58367, 8191,65281, 15, 0,40942,65529,65023,50117, 6559,45184, 15, 3, 34788,65529,65023,50029, 6535,24064, 0, 31,45038,65531,65023,58349, 7103, 1, 1, 0, 40942,65529,65023,58317, 6543,45248, 3, 0,51180,54845,50968,50111, 7623, 128, 0, 0, 57326,65533,65023,50159, 7647, 96, 3, 0,57324,65533,65023,50159, 7647,16480, 3, 0, 57324,65533,65023,50175, 7631, 128, 3, 0,65516,64639,65535,12283,32895,65375, 0, 12, 65534,65535,65535, 2047,32767, 0, 0, 0, 9622,65264,60590,15359, 8223,12288, 0, 0, 1, 0, 0, 0,65279,65535, 2047,65534, 3843,65279,65535, 8191, 0, 0, 0, 0, 65535,65535,63227, 327, 0, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 127, 65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, 65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, 32767,32573,65535,65535,65407, 2047, 0, 0, 0, 0,65535,65535,65535,65535,65535, 31, 65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 65535,65535,65535,65535,65535,65535,40959, 127,65534, 2047,65535,65535,65535,65535, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 0, 0, 0, 0, 0,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, 65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, 64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, 192, 0, 1022, 1792,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 2047, 65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, 65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, 65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, 65535,65535,65535,16383, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, 0, 0, 0, 0, 0, 0, 0,65495,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0,65534, 2047,65534, 2047,65472,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; const UInt16 UnicodeDigitBits[] = { 256, 0, 0, 0, 0, 0, 272, 0, 0, 288, 304, 320, 336, 352, 368, 384, 400, 0, 0, 416, 0, 0, 0, 432, 448, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 464, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 524, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65408, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0,65472, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65024, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const UInt16 UnicodeSpaceBits[] = { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15872, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 4095, 0,33536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; const UInt16 UnicodeXDigitBits[] = { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1023, 126, 0, 126, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Uncomment if necessary //const UInt16 UnicodeCntrlBits[] = { // 256, 0, 0, 0, 0, 0, 0, 272, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 288, 0, 0, 0, 0, 0, 0, 0, // 304, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 320, 336, //65535,65535, 0, 0, 0, 0, 0,32768,65535,65535, 0, 0, 0, 0, 0, 0, //32768, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30720, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //61440, 0,31744, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32768, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3584}; // //const UInt16 UnicodeGraphBits[] = { // 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, // 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, // 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, // 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, // 0, 0,65534,65535,65535,65535,65535,32767, 0, 0,65534,65535,65535,65535,65535,65535, //65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, // 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, //65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, // 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, // 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, //16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, //65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, //34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, //40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, //57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, //57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, //65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, //65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, //65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, //65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, //65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, //32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, //65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, //65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, // 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, // 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, //65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, // 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, //64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65486,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, //65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, //65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, //65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, // 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535, 8191, //63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; // //const UInt16 UnicodePrintBits[] = { // 256, 1, 272, 288, 304, 320, 336, 352, 0, 368, 384, 400, 416, 432, 448, 464, // 480, 496, 512, 528, 544, 1, 560, 576, 592, 0, 0, 0, 0, 0, 608, 624, // 640, 656, 0, 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 688, 704, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 720, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 736, // 1, 1, 1, 1, 752, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 768, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 784, 800, 1, 816, 832, 848, // 512, 0,65535,65535,65535,65535,65535,32767, 0, 0,65535,65535,65535,65535,65535,65535, //65535,65535,65532, 15, 0,65535,65535,65535,65535,65535,16383,63999, 3, 0,16415, 0, // 0, 0, 0, 0, 32, 0, 0,17408,55232,65535,65531,65535,32767,64767,65535, 15, //65535,65535,65535,65535,65535,65535,65535,65535,61443,65535,65535,65535, 6559,65535,65535, 831, // 0, 0, 0,65534,65535,65151,65534,65535, 1791, 0, 0,16384, 9,65535, 2047, 31, // 4096,34816,65534, 2047,65534, 63,16383,65535,65535,65535,65535,65535,65535, 8191, 8702, 8191, //16383,65535, 8191,65535, 0, 0, 0, 0,65535,65535,65535, 1, 0, 0, 0, 0, //65518,65535,65535,58367, 8191,65281,65535, 1,40942,65529,65023,50117, 6559,45184,65487, 3, //34788,65529,65023,50029, 6535,24064,65472, 31,45038,65531,65023,58349, 7103, 1,65473, 0, //40942,65529,65023,58317, 6543,45248,65475, 0,51180,54845,50968,50111, 7623, 128,65408, 0, //57326,65533,65023,50159, 7647, 96,65475, 0,57324,65533,65023,50159, 7647,16480,65475, 0, //57324,65533,65023,50175, 7631, 128,65475, 0,65516,64639,65535,12283,32895,65375, 0, 28, //65534,65535,65535, 2047,65535, 4095, 0, 0, 9622,65264,60590,15359, 8223,13311, 0, 0, //65521, 7, 1023,15360,65279,65535, 2047,65534, 3875,65279,65535, 8191, 0, 0, 0, 0, //65535,65535,63227, 327,65535, 1023, 0, 0, 0, 0,65535,65535, 63,65535,65535, 2175, //65535,65535,65535,65535,65535,33791,65535,65535,65535,65535,65287,65535,65535,65535,65535, 1023, //65407,65535,65535,65535,15743,15743,65535,65535,15743,65535,32767,32573,32573,65407,32767,65535, //32767,32573,65535,65535,65407, 2047,65534, 3, 0, 0,65535,65535,65535,65535,65535, 31, //65534,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, //65535,65535,65535,65535,65535,65535,65535, 127,65534, 8191,65535,65535,65535,65535,16383, 0, // 0, 0, 0, 0, 0, 0, 0, 0,65535,65535,65535,65535, 511, 6128, 1023, 0, // 2047, 1023,65535,65535,65527,65535,65535, 255,65535,65535, 1023, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535, 4095,65535,65535,65535,65535,65535, 1023, //65535,16191,65535,65535,16191,43775,65535,16383,65535,65535,65535,24543, 8156, 4047, 8191, 8156, // 0,65535, 255,65535,16239, 0, 0,57344,24576, 0, 0, 0, 0, 0, 0, 0, //64644,15919,48464, 1019, 0, 0,65535,65535, 15, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65487,65523, 1022, 1793,65534,65535,65535,65535,65535, 31,65534,65535,65535,65535,65535, 4095, //65504,65535, 8191,65534,65535,65535,65535,65535,32767, 0,65535, 255, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 63, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535, 8191, 0, 0, 0, 0, 0, 0, 0, //65535,65535,65535,65535,65535,65535,65535,65535,65535,65535, 15, 0, 0, 0, 0, 0, //65535,65535,16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127,41208,65023,24447,65499,65535,65535,65535,65535,65535,65535, 3, 0,65528,65535,65535, //65535,65535,65535,65535, 0,65535,65535,65535,65535,65532,65535,65535, 255, 0, 0, 4095, // 0, 0, 0,65535,65055,65527, 3339,65495,65535,65535,65535,65535,65535,65535,65535,40959, //63470,36863,65535,49151,65534,12287,65534,65534,65535,16383,65535,32767,64764, 7420, 0, 0}; // //const UInt16 UnicodePunctBits[] = { // 256, 0, 0, 272, 0, 288, 304, 320, 0, 336, 0, 0, 0, 352, 368, 384, // 400, 0, 0, 416, 0, 0, 432, 448, 464, 0, 0, 0, 0, 0, 0, 0, // 480, 0, 0, 496, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 528, 544, 560, // 0, 0,65534,64512, 1,63488, 1,30720, 0, 0,65534,65535, 0, 128, 0, 128, // 0, 0, 0, 0, 0, 0, 0,16384, 128, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0,64512, 0, 0, 1536, 0, 0,16384, 9, 0, 0, 24, // 4096,34816, 0, 0, 0, 0,15360, 0, 0, 0, 0, 0, 0, 16, 0, 0, //16383, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, // 0, 0, 0, 0,32768, 3072, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65520, 7, 0,15360, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0,64512, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, // 0, 0, 0, 0, 0, 0, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0,24576, 0, 0, 6144, 0, 0, 0, 0,14336, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6128, 0, 0, // 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0,65535, 255,65535,16239, 0, 0,24576,24576, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 1536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //65294,65523, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2048, // 0, 0, 0,49152, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0,65535,65055,65527, 3339, 0, 0, 0, 0, 0, 0, 0, 0, 0, //63470,35840, 1,47104, 0,10240, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // //const UInt16 UnicodeLowerBits[] = { // 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, // 384, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 432, // 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0,32768,65535,65407, //43690,43690,43690,21930,43861,43690,43690,54442,12585,20004,11562,58961,23392,46421,43690,43565, //43690,43690,43688, 10, 0,65535,65535,65535,65535,65535,16383, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,61440,65535,32767,43235,43690, 15, // 0, 0, 0,65535,65535,65535,43690,43690,40962,43690,43690,43690, 4372,43690,43690, 554, // 0, 0, 0, 0, 0, 0,65534,65535, 255, 0, 0, 0, 0, 0, 0, 0, //43690,43690,43690,43690,43690,43690,43690,43690,43690, 4074,43690,43690,43690,43690,43690, 682, // 255, 63, 255, 255, 63, 255, 255,16383,65535,65535,65535,20703, 4316, 207, 255, 4316, // 0, 0, 0, 0, 0, 0, 0,32768, 0, 0, 0, 0, 0, 0, 0, 0, //50176, 8,32768, 528, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 127, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // //const UInt16 UnicodeUpperBits[] = { // 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, // 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, // 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, //21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53973, 4526,44464,19114,21845,21974, //21845,21845,21844, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0,21532,21845, 0, //65535,65535,65535, 0, 0, 0,21845,21845,20481,21845,21845,21845, 2187,21845,21845, 277, // 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, //21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, //65280,16128,65280,65280,16128,43520,65280, 0,65280,65280,65280, 7936, 7936, 3840, 7936, 7936, //14468,15911,15696, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // MA: March 19, 2010 // Modified ToUpper and ToLower tables to match values expected by AS3 tests. // ToLower modifications: // 304 -> 105 // 1024 -> 1104 * // 1037 -> 1117 * // UoUpper modifications: // 255 -> 376 // 305 -> 73 // 383 -> 83 // 1104 -> 1024 * // 1117 -> 1037 * // Entries marked with a '*' don't make complete sense based on Unicode manual, although // they match AS3. static const UInt16 UnicodeToUpperBits[] = { 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 368, 0, 384, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 416, 0, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,65407, 43690,43690,43690,21674,43349,43690,43690,54442, 4392, 516, 8490, 8785,21056,46421,43690,43048, // MA: Modified for AS3. 43690, 170, 0, 0, 0, 2776,33545, 36, 3336, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,61440,65534,32767, 0,43688, 0, 0, 0, 0,65535,65535,65535,43690,43690, 2,43690,43690,43690, 4372,43690,35498, 554, // MA: Modified for AS3. 0, 0, 0, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 43690,43690,43690,43690,43690,43690,43690,43690,43690, 42,43690,43690,43690,43690,43690, 682, 255, 63, 255, 255, 63, 170, 255,16383, 0, 0, 0, 3, 0, 3, 35, 0, 0, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535, 1023, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const UInt16 UnicodeToLowerBits[] = { 256, 272, 288, 304, 320, 336, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 368, 384, 0, 400, 0, 0, 416, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 432, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0,65535,32639, 0, 0, 21845,21845,21845,43605,21674,21845,21845,11093,52950,45531,53909, 4526,42128,19114,21845,21522,// MA: Modidied for AS3. 21845, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,55104,65534, 4091, 0, 0, 0,21844, 0, 65535,65535,65535, 0, 0, 0,21845,21845, 1,21845,21845,21845, 2186,21845,17749, 277, 0, 0, 0,65534,65535, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65535,65535, 63, 0, 0, 0, 21845,21845,21845,21845,21845,21845,21845,21845,21845, 21,21845,21845,21845,21845,21845, 341, 65280,16128,65280,65280,16128,43520,65280, 0, 0, 0, 0, 3840, 3840, 3840, 7936, 3840, 0, 0, 0, 0, 0, 0,65535, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,65472,65535, 0, 0, 0, 0, 0,65534, 2047, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; struct GUnicodePairType { UInt16 Key, Value; }; static inline bool CmpUnicodeKey(const GUnicodePairType& a, UInt16 key) { return a.Key < key; } static const GUnicodePairType UnicodeToUpperTable[] = { { 97, 65}, { 98, 66}, { 99, 67}, { 100, 68}, { 101, 69}, { 102, 70}, { 103, 71}, { 104, 72}, { 105, 73}, { 106, 74}, { 107, 75}, { 108, 76}, { 109, 77}, { 110, 78}, { 111, 79}, { 112, 80}, { 113, 81}, { 114, 82}, { 115, 83}, { 116, 84}, { 117, 85}, { 118, 86}, { 119, 87}, { 120, 88}, { 121, 89}, { 122, 90}, { 224, 192}, { 225, 193}, { 226, 194}, { 227, 195}, { 228, 196}, { 229, 197}, { 230, 198}, { 231, 199}, { 232, 200}, { 233, 201}, { 234, 202}, { 235, 203}, { 236, 204}, { 237, 205}, { 238, 206}, { 239, 207}, { 240, 208}, { 241, 209}, { 242, 210}, { 243, 211}, { 244, 212}, { 245, 213}, { 246, 214}, { 248, 216}, { 249, 217}, { 250, 218}, { 251, 219}, { 252, 220}, { 253, 221}, { 254, 222}, { 255, 376}, { 257, 256}, { 259, 258}, { 261, 260}, { 263, 262}, { 265, 264}, { 267, 266}, { 269, 268}, { 271, 270}, { 273, 272}, { 275, 274}, { 277, 276}, { 279, 278}, { 281, 280}, { 283, 282}, { 285, 284}, { 287, 286}, { 289, 288}, { 291, 290}, { 293, 292}, { 295, 294}, { 297, 296}, { 299, 298}, { 301, 300}, { 303, 302}, { 305, 73}, { 307, 306}, { 309, 308}, { 311, 310}, { 314, 313}, { 316, 315}, { 318, 317}, { 320, 319}, { 322, 321}, { 324, 323}, { 326, 325}, { 328, 327}, { 331, 330}, { 333, 332}, { 335, 334}, { 337, 336}, { 339, 338}, { 341, 340}, { 343, 342}, { 345, 344}, { 347, 346}, { 349, 348}, { 351, 350}, { 353, 352}, { 355, 354}, { 357, 356}, { 359, 358}, { 361, 360}, { 363, 362}, { 365, 364}, { 367, 366}, { 369, 368}, { 371, 370}, { 373, 372}, { 375, 374}, { 378, 377}, { 380, 379}, { 382, 381}, { 383, 83}, { 387, 386}, { 389, 388}, { 392, 391}, { 396, 395}, { 402, 401}, { 409, 408}, { 417, 416}, { 419, 418}, { 421, 420}, { 424, 423}, { 429, 428}, { 432, 431}, { 436, 435}, { 438, 437}, { 441, 440}, { 445, 444}, { 454, 452}, { 457, 455}, { 460, 458}, { 462, 461}, { 464, 463}, { 466, 465}, { 468, 467}, { 470, 469}, { 472, 471}, { 474, 473}, { 476, 475}, { 477, 398}, { 479, 478}, { 481, 480}, { 483, 482}, { 485, 484}, { 487, 486}, { 489, 488}, { 491, 490}, { 493, 492}, { 495, 494}, { 499, 497}, { 501, 500}, { 507, 506}, { 509, 508}, { 511, 510}, { 513, 512}, { 515, 514}, { 517, 516}, { 519, 518}, { 521, 520}, { 523, 522}, { 525, 524}, { 527, 526}, { 529, 528}, { 531, 530}, { 533, 532}, { 535, 534}, { 595, 385}, { 596, 390}, { 598, 393}, { 599, 394}, { 601, 399}, { 603, 400}, { 608, 403}, { 611, 404}, { 616, 407}, { 617, 406}, { 623, 412}, { 626, 413}, { 629, 415}, { 643, 425}, { 648, 430}, { 650, 433}, { 651, 434}, { 658, 439}, { 940, 902}, { 941, 904}, { 942, 905}, { 943, 906}, { 945, 913}, { 946, 914}, { 947, 915}, { 948, 916}, { 949, 917}, { 950, 918}, { 951, 919}, { 952, 920}, { 953, 921}, { 954, 922}, { 955, 923}, { 956, 924}, { 957, 925}, { 958, 926}, { 959, 927}, { 960, 928}, { 961, 929}, { 962, 931}, { 963, 931}, { 964, 932}, { 965, 933}, { 966, 934}, { 967, 935}, { 968, 936}, { 969, 937}, { 970, 938}, { 971, 939}, { 972, 908}, { 973, 910}, { 974, 911}, { 995, 994}, { 997, 996}, { 999, 998}, { 1001, 1000}, { 1003, 1002}, { 1005, 1004}, { 1007, 1006}, { 1072, 1040}, { 1073, 1041}, { 1074, 1042}, { 1075, 1043}, { 1076, 1044}, { 1077, 1045}, { 1078, 1046}, { 1079, 1047}, { 1080, 1048}, { 1081, 1049}, { 1082, 1050}, { 1083, 1051}, { 1084, 1052}, { 1085, 1053}, { 1086, 1054}, { 1087, 1055}, { 1088, 1056}, { 1089, 1057}, { 1090, 1058}, { 1091, 1059}, { 1092, 1060}, { 1093, 1061}, { 1094, 1062}, { 1095, 1063}, { 1096, 1064}, { 1097, 1065}, { 1098, 1066}, { 1099, 1067}, { 1100, 1068}, { 1101, 1069}, { 1102, 1070}, { 1103, 1071}, { 1104, 1024}, { 1105, 1025}, { 1106, 1026}, { 1107, 1027}, { 1108, 1028}, { 1109, 1029}, { 1110, 1030}, { 1111, 1031}, { 1112, 1032}, { 1113, 1033}, { 1114, 1034}, { 1115, 1035}, { 1116, 1036}, { 1117, 1037}, { 1118, 1038}, { 1119, 1039}, { 1121, 1120}, { 1123, 1122}, { 1125, 1124}, { 1127, 1126}, { 1129, 1128}, { 1131, 1130}, { 1133, 1132}, { 1135, 1134}, { 1137, 1136}, { 1139, 1138}, { 1141, 1140}, { 1143, 1142}, { 1145, 1144}, { 1147, 1146}, { 1149, 1148}, { 1151, 1150}, { 1153, 1152}, { 1169, 1168}, { 1171, 1170}, { 1173, 1172}, { 1175, 1174}, { 1177, 1176}, { 1179, 1178}, { 1181, 1180}, { 1183, 1182}, { 1185, 1184}, { 1187, 1186}, { 1189, 1188}, { 1191, 1190}, { 1193, 1192}, { 1195, 1194}, { 1197, 1196}, { 1199, 1198}, { 1201, 1200}, { 1203, 1202}, { 1205, 1204}, { 1207, 1206}, { 1209, 1208}, { 1211, 1210}, { 1213, 1212}, { 1215, 1214}, { 1218, 1217}, { 1220, 1219}, { 1224, 1223}, { 1228, 1227}, { 1233, 1232}, { 1235, 1234}, { 1237, 1236}, { 1239, 1238}, { 1241, 1240}, { 1243, 1242}, { 1245, 1244}, { 1247, 1246}, { 1249, 1248}, { 1251, 1250}, { 1253, 1252}, { 1255, 1254}, { 1257, 1256}, { 1259, 1258}, { 1263, 1262}, { 1265, 1264}, { 1267, 1266}, { 1269, 1268}, { 1273, 1272}, { 1377, 1329}, { 1378, 1330}, { 1379, 1331}, { 1380, 1332}, { 1381, 1333}, { 1382, 1334}, { 1383, 1335}, { 1384, 1336}, { 1385, 1337}, { 1386, 1338}, { 1387, 1339}, { 1388, 1340}, { 1389, 1341}, { 1390, 1342}, { 1391, 1343}, { 1392, 1344}, { 1393, 1345}, { 1394, 1346}, { 1395, 1347}, { 1396, 1348}, { 1397, 1349}, { 1398, 1350}, { 1399, 1351}, { 1400, 1352}, { 1401, 1353}, { 1402, 1354}, { 1403, 1355}, { 1404, 1356}, { 1405, 1357}, { 1406, 1358}, { 1407, 1359}, { 1408, 1360}, { 1409, 1361}, { 1410, 1362}, { 1411, 1363}, { 1412, 1364}, { 1413, 1365}, { 1414, 1366}, { 7681, 7680}, { 7683, 7682}, { 7685, 7684}, { 7687, 7686}, { 7689, 7688}, { 7691, 7690}, { 7693, 7692}, { 7695, 7694}, { 7697, 7696}, { 7699, 7698}, { 7701, 7700}, { 7703, 7702}, { 7705, 7704}, { 7707, 7706}, { 7709, 7708}, { 7711, 7710}, { 7713, 7712}, { 7715, 7714}, { 7717, 7716}, { 7719, 7718}, { 7721, 7720}, { 7723, 7722}, { 7725, 7724}, { 7727, 7726}, { 7729, 7728}, { 7731, 7730}, { 7733, 7732}, { 7735, 7734}, { 7737, 7736}, { 7739, 7738}, { 7741, 7740}, { 7743, 7742}, { 7745, 7744}, { 7747, 7746}, { 7749, 7748}, { 7751, 7750}, { 7753, 7752}, { 7755, 7754}, { 7757, 7756}, { 7759, 7758}, { 7761, 7760}, { 7763, 7762}, { 7765, 7764}, { 7767, 7766}, { 7769, 7768}, { 7771, 7770}, { 7773, 7772}, { 7775, 7774}, { 7777, 7776}, { 7779, 7778}, { 7781, 7780}, { 7783, 7782}, { 7785, 7784}, { 7787, 7786}, { 7789, 7788}, { 7791, 7790}, { 7793, 7792}, { 7795, 7794}, { 7797, 7796}, { 7799, 7798}, { 7801, 7800}, { 7803, 7802}, { 7805, 7804}, { 7807, 7806}, { 7809, 7808}, { 7811, 7810}, { 7813, 7812}, { 7815, 7814}, { 7817, 7816}, { 7819, 7818}, { 7821, 7820}, { 7823, 7822}, { 7825, 7824}, { 7827, 7826}, { 7829, 7828}, { 7841, 7840}, { 7843, 7842}, { 7845, 7844}, { 7847, 7846}, { 7849, 7848}, { 7851, 7850}, { 7853, 7852}, { 7855, 7854}, { 7857, 7856}, { 7859, 7858}, { 7861, 7860}, { 7863, 7862}, { 7865, 7864}, { 7867, 7866}, { 7869, 7868}, { 7871, 7870}, { 7873, 7872}, { 7875, 7874}, { 7877, 7876}, { 7879, 7878}, { 7881, 7880}, { 7883, 7882}, { 7885, 7884}, { 7887, 7886}, { 7889, 7888}, { 7891, 7890}, { 7893, 7892}, { 7895, 7894}, { 7897, 7896}, { 7899, 7898}, { 7901, 7900}, { 7903, 7902}, { 7905, 7904}, { 7907, 7906}, { 7909, 7908}, { 7911, 7910}, { 7913, 7912}, { 7915, 7914}, { 7917, 7916}, { 7919, 7918}, { 7921, 7920}, { 7923, 7922}, { 7925, 7924}, { 7927, 7926}, { 7929, 7928}, { 7936, 7944}, { 7937, 7945}, { 7938, 7946}, { 7939, 7947}, { 7940, 7948}, { 7941, 7949}, { 7942, 7950}, { 7943, 7951}, { 7952, 7960}, { 7953, 7961}, { 7954, 7962}, { 7955, 7963}, { 7956, 7964}, { 7957, 7965}, { 7968, 7976}, { 7969, 7977}, { 7970, 7978}, { 7971, 7979}, { 7972, 7980}, { 7973, 7981}, { 7974, 7982}, { 7975, 7983}, { 7984, 7992}, { 7985, 7993}, { 7986, 7994}, { 7987, 7995}, { 7988, 7996}, { 7989, 7997}, { 7990, 7998}, { 7991, 7999}, { 8000, 8008}, { 8001, 8009}, { 8002, 8010}, { 8003, 8011}, { 8004, 8012}, { 8005, 8013}, { 8017, 8025}, { 8019, 8027}, { 8021, 8029}, { 8023, 8031}, { 8032, 8040}, { 8033, 8041}, { 8034, 8042}, { 8035, 8043}, { 8036, 8044}, { 8037, 8045}, { 8038, 8046}, { 8039, 8047}, { 8048, 8122}, { 8049, 8123}, { 8050, 8136}, { 8051, 8137}, { 8052, 8138}, { 8053, 8139}, { 8054, 8154}, { 8055, 8155}, { 8056, 8184}, { 8057, 8185}, { 8058, 8170}, { 8059, 8171}, { 8060, 8186}, { 8061, 8187}, { 8112, 8120}, { 8113, 8121}, { 8144, 8152}, { 8145, 8153}, { 8160, 8168}, { 8161, 8169}, { 8165, 8172}, { 8560, 8544}, { 8561, 8545}, { 8562, 8546}, { 8563, 8547}, { 8564, 8548}, { 8565, 8549}, { 8566, 8550}, { 8567, 8551}, { 8568, 8552}, { 8569, 8553}, { 8570, 8554}, { 8571, 8555}, { 8572, 8556}, { 8573, 8557}, { 8574, 8558}, { 8575, 8559}, { 9424, 9398}, { 9425, 9399}, { 9426, 9400}, { 9427, 9401}, { 9428, 9402}, { 9429, 9403}, { 9430, 9404}, { 9431, 9405}, { 9432, 9406}, { 9433, 9407}, { 9434, 9408}, { 9435, 9409}, { 9436, 9410}, { 9437, 9411}, { 9438, 9412}, { 9439, 9413}, { 9440, 9414}, { 9441, 9415}, { 9442, 9416}, { 9443, 9417}, { 9444, 9418}, { 9445, 9419}, { 9446, 9420}, { 9447, 9421}, { 9448, 9422}, { 9449, 9423}, {65345,65313}, {65346,65314}, {65347,65315}, {65348,65316}, {65349,65317}, {65350,65318}, {65351,65319}, {65352,65320}, {65353,65321}, {65354,65322}, {65355,65323}, {65356,65324}, {65357,65325}, {65358,65326}, {65359,65327}, {65360,65328}, {65361,65329}, {65362,65330}, {65363,65331}, {65364,65332}, {65365,65333}, {65366,65334}, {65367,65335}, {65368,65336}, {65369,65337}, {65370,65338}, {65535, 0}}; static const GUnicodePairType UnicodeToLowerTable[] = { { 65, 97}, { 66, 98}, { 67, 99}, { 68, 100}, { 69, 101}, { 70, 102}, { 71, 103}, { 72, 104}, { 73, 105}, { 74, 106}, { 75, 107}, { 76, 108}, { 77, 109}, { 78, 110}, { 79, 111}, { 80, 112}, { 81, 113}, { 82, 114}, { 83, 115}, { 84, 116}, { 85, 117}, { 86, 118}, { 87, 119}, { 88, 120}, { 89, 121}, { 90, 122}, { 192, 224}, { 193, 225}, { 194, 226}, { 195, 227}, { 196, 228}, { 197, 229}, { 198, 230}, { 199, 231}, { 200, 232}, { 201, 233}, { 202, 234}, { 203, 235}, { 204, 236}, { 205, 237}, { 206, 238}, { 207, 239}, { 208, 240}, { 209, 241}, { 210, 242}, { 211, 243}, { 212, 244}, { 213, 245}, { 214, 246}, { 216, 248}, { 217, 249}, { 218, 250}, { 219, 251}, { 220, 252}, { 221, 253}, { 222, 254}, { 256, 257}, { 258, 259}, { 260, 261}, { 262, 263}, { 264, 265}, { 266, 267}, { 268, 269}, { 270, 271}, { 272, 273}, { 274, 275}, { 276, 277}, { 278, 279}, { 280, 281}, { 282, 283}, { 284, 285}, { 286, 287}, { 288, 289}, { 290, 291}, { 292, 293}, { 294, 295}, { 296, 297}, { 298, 299}, { 300, 301}, { 302, 303}, { 304, 105}, { 306, 307}, { 308, 309}, { 310, 311}, { 313, 314}, { 315, 316}, { 317, 318}, { 319, 320}, { 321, 322}, { 323, 324}, { 325, 326}, { 327, 328}, { 330, 331}, { 332, 333}, { 334, 335}, { 336, 337}, { 338, 339}, { 340, 341}, { 342, 343}, { 344, 345}, { 346, 347}, { 348, 349}, { 350, 351}, { 352, 353}, { 354, 355}, { 356, 357}, { 358, 359}, { 360, 361}, { 362, 363}, { 364, 365}, { 366, 367}, { 368, 369}, { 370, 371}, { 372, 373}, { 374, 375}, { 376, 255}, { 377, 378}, { 379, 380}, { 381, 382}, { 385, 595}, { 386, 387}, { 388, 389}, { 390, 596}, { 391, 392}, { 393, 598}, { 394, 599}, { 395, 396}, { 398, 477}, { 399, 601}, { 400, 603}, { 401, 402}, { 403, 608}, { 404, 611}, { 406, 617}, { 407, 616}, { 408, 409}, { 412, 623}, { 413, 626}, { 415, 629}, { 416, 417}, { 418, 419}, { 420, 421}, { 423, 424}, { 425, 643}, { 428, 429}, { 430, 648}, { 431, 432}, { 433, 650}, { 434, 651}, { 435, 436}, { 437, 438}, { 439, 658}, { 440, 441}, { 444, 445}, { 452, 454}, { 455, 457}, { 458, 460}, { 461, 462}, { 463, 464}, { 465, 466}, { 467, 468}, { 469, 470}, { 471, 472}, { 473, 474}, { 475, 476}, { 478, 479}, { 480, 481}, { 482, 483}, { 484, 485}, { 486, 487}, { 488, 489}, { 490, 491}, { 492, 493}, { 494, 495}, { 497, 499}, { 500, 501}, { 506, 507}, { 508, 509}, { 510, 511}, { 512, 513}, { 514, 515}, { 516, 517}, { 518, 519}, { 520, 521}, { 522, 523}, { 524, 525}, { 526, 527}, { 528, 529}, { 530, 531}, { 532, 533}, { 534, 535}, { 902, 940}, { 904, 941}, { 905, 942}, { 906, 943}, { 908, 972}, { 910, 973}, { 911, 974}, { 913, 945}, { 914, 946}, { 915, 947}, { 916, 948}, { 917, 949}, { 918, 950}, { 919, 951}, { 920, 952}, { 921, 953}, { 922, 954}, { 923, 955}, { 924, 956}, { 925, 957}, { 926, 958}, { 927, 959}, { 928, 960}, { 929, 961}, { 931, 963}, { 932, 964}, { 933, 965}, { 934, 966}, { 935, 967}, { 936, 968}, { 937, 969}, { 938, 970}, { 939, 971}, { 994, 995}, { 996, 997}, { 998, 999}, { 1000, 1001}, { 1002, 1003}, { 1004, 1005}, { 1006, 1007}, { 1024, 1104}, { 1025, 1105}, { 1026, 1106}, { 1027, 1107}, { 1028, 1108}, { 1029, 1109}, { 1030, 1110}, { 1031, 1111}, { 1032, 1112}, { 1033, 1113}, { 1034, 1114}, { 1035, 1115}, { 1036, 1116}, { 1037, 1117}, { 1038, 1118}, { 1039, 1119}, { 1040, 1072}, { 1041, 1073}, { 1042, 1074}, { 1043, 1075}, { 1044, 1076}, { 1045, 1077}, { 1046, 1078}, { 1047, 1079}, { 1048, 1080}, { 1049, 1081}, { 1050, 1082}, { 1051, 1083}, { 1052, 1084}, { 1053, 1085}, { 1054, 1086}, { 1055, 1087}, { 1056, 1088}, { 1057, 1089}, { 1058, 1090}, { 1059, 1091}, { 1060, 1092}, { 1061, 1093}, { 1062, 1094}, { 1063, 1095}, { 1064, 1096}, { 1065, 1097}, { 1066, 1098}, { 1067, 1099}, { 1068, 1100}, { 1069, 1101}, { 1070, 1102}, { 1071, 1103}, { 1120, 1121}, { 1122, 1123}, { 1124, 1125}, { 1126, 1127}, { 1128, 1129}, { 1130, 1131}, { 1132, 1133}, { 1134, 1135}, { 1136, 1137}, { 1138, 1139}, { 1140, 1141}, { 1142, 1143}, { 1144, 1145}, { 1146, 1147}, { 1148, 1149}, { 1150, 1151}, { 1152, 1153}, { 1168, 1169}, { 1170, 1171}, { 1172, 1173}, { 1174, 1175}, { 1176, 1177}, { 1178, 1179}, { 1180, 1181}, { 1182, 1183}, { 1184, 1185}, { 1186, 1187}, { 1188, 1189}, { 1190, 1191}, { 1192, 1193}, { 1194, 1195}, { 1196, 1197}, { 1198, 1199}, { 1200, 1201}, { 1202, 1203}, { 1204, 1205}, { 1206, 1207}, { 1208, 1209}, { 1210, 1211}, { 1212, 1213}, { 1214, 1215}, { 1217, 1218}, { 1219, 1220}, { 1223, 1224}, { 1227, 1228}, { 1232, 1233}, { 1234, 1235}, { 1236, 1237}, { 1238, 1239}, { 1240, 1241}, { 1242, 1243}, { 1244, 1245}, { 1246, 1247}, { 1248, 1249}, { 1250, 1251}, { 1252, 1253}, { 1254, 1255}, { 1256, 1257}, { 1258, 1259}, { 1262, 1263}, { 1264, 1265}, { 1266, 1267}, { 1268, 1269}, { 1272, 1273}, { 1329, 1377}, { 1330, 1378}, { 1331, 1379}, { 1332, 1380}, { 1333, 1381}, { 1334, 1382}, { 1335, 1383}, { 1336, 1384}, { 1337, 1385}, { 1338, 1386}, { 1339, 1387}, { 1340, 1388}, { 1341, 1389}, { 1342, 1390}, { 1343, 1391}, { 1344, 1392}, { 1345, 1393}, { 1346, 1394}, { 1347, 1395}, { 1348, 1396}, { 1349, 1397}, { 1350, 1398}, { 1351, 1399}, { 1352, 1400}, { 1353, 1401}, { 1354, 1402}, { 1355, 1403}, { 1356, 1404}, { 1357, 1405}, { 1358, 1406}, { 1359, 1407}, { 1360, 1408}, { 1361, 1409}, { 1362, 1410}, { 1363, 1411}, { 1364, 1412}, { 1365, 1413}, { 1366, 1414}, { 4256, 4304}, { 4257, 4305}, { 4258, 4306}, { 4259, 4307}, { 4260, 4308}, { 4261, 4309}, { 4262, 4310}, { 4263, 4311}, { 4264, 4312}, { 4265, 4313}, { 4266, 4314}, { 4267, 4315}, { 4268, 4316}, { 4269, 4317}, { 4270, 4318}, { 4271, 4319}, { 4272, 4320}, { 4273, 4321}, { 4274, 4322}, { 4275, 4323}, { 4276, 4324}, { 4277, 4325}, { 4278, 4326}, { 4279, 4327}, { 4280, 4328}, { 4281, 4329}, { 4282, 4330}, { 4283, 4331}, { 4284, 4332}, { 4285, 4333}, { 4286, 4334}, { 4287, 4335}, { 4288, 4336}, { 4289, 4337}, { 4290, 4338}, { 4291, 4339}, { 4292, 4340}, { 4293, 4341}, { 7680, 7681}, { 7682, 7683}, { 7684, 7685}, { 7686, 7687}, { 7688, 7689}, { 7690, 7691}, { 7692, 7693}, { 7694, 7695}, { 7696, 7697}, { 7698, 7699}, { 7700, 7701}, { 7702, 7703}, { 7704, 7705}, { 7706, 7707}, { 7708, 7709}, { 7710, 7711}, { 7712, 7713}, { 7714, 7715}, { 7716, 7717}, { 7718, 7719}, { 7720, 7721}, { 7722, 7723}, { 7724, 7725}, { 7726, 7727}, { 7728, 7729}, { 7730, 7731}, { 7732, 7733}, { 7734, 7735}, { 7736, 7737}, { 7738, 7739}, { 7740, 7741}, { 7742, 7743}, { 7744, 7745}, { 7746, 7747}, { 7748, 7749}, { 7750, 7751}, { 7752, 7753}, { 7754, 7755}, { 7756, 7757}, { 7758, 7759}, { 7760, 7761}, { 7762, 7763}, { 7764, 7765}, { 7766, 7767}, { 7768, 7769}, { 7770, 7771}, { 7772, 7773}, { 7774, 7775}, { 7776, 7777}, { 7778, 7779}, { 7780, 7781}, { 7782, 7783}, { 7784, 7785}, { 7786, 7787}, { 7788, 7789}, { 7790, 7791}, { 7792, 7793}, { 7794, 7795}, { 7796, 7797}, { 7798, 7799}, { 7800, 7801}, { 7802, 7803}, { 7804, 7805}, { 7806, 7807}, { 7808, 7809}, { 7810, 7811}, { 7812, 7813}, { 7814, 7815}, { 7816, 7817}, { 7818, 7819}, { 7820, 7821}, { 7822, 7823}, { 7824, 7825}, { 7826, 7827}, { 7828, 7829}, { 7840, 7841}, { 7842, 7843}, { 7844, 7845}, { 7846, 7847}, { 7848, 7849}, { 7850, 7851}, { 7852, 7853}, { 7854, 7855}, { 7856, 7857}, { 7858, 7859}, { 7860, 7861}, { 7862, 7863}, { 7864, 7865}, { 7866, 7867}, { 7868, 7869}, { 7870, 7871}, { 7872, 7873}, { 7874, 7875}, { 7876, 7877}, { 7878, 7879}, { 7880, 7881}, { 7882, 7883}, { 7884, 7885}, { 7886, 7887}, { 7888, 7889}, { 7890, 7891}, { 7892, 7893}, { 7894, 7895}, { 7896, 7897}, { 7898, 7899}, { 7900, 7901}, { 7902, 7903}, { 7904, 7905}, { 7906, 7907}, { 7908, 7909}, { 7910, 7911}, { 7912, 7913}, { 7914, 7915}, { 7916, 7917}, { 7918, 7919}, { 7920, 7921}, { 7922, 7923}, { 7924, 7925}, { 7926, 7927}, { 7928, 7929}, { 7944, 7936}, { 7945, 7937}, { 7946, 7938}, { 7947, 7939}, { 7948, 7940}, { 7949, 7941}, { 7950, 7942}, { 7951, 7943}, { 7960, 7952}, { 7961, 7953}, { 7962, 7954}, { 7963, 7955}, { 7964, 7956}, { 7965, 7957}, { 7976, 7968}, { 7977, 7969}, { 7978, 7970}, { 7979, 7971}, { 7980, 7972}, { 7981, 7973}, { 7982, 7974}, { 7983, 7975}, { 7992, 7984}, { 7993, 7985}, { 7994, 7986}, { 7995, 7987}, { 7996, 7988}, { 7997, 7989}, { 7998, 7990}, { 7999, 7991}, { 8008, 8000}, { 8009, 8001}, { 8010, 8002}, { 8011, 8003}, { 8012, 8004}, { 8013, 8005}, { 8025, 8017}, { 8027, 8019}, { 8029, 8021}, { 8031, 8023}, { 8040, 8032}, { 8041, 8033}, { 8042, 8034}, { 8043, 8035}, { 8044, 8036}, { 8045, 8037}, { 8046, 8038}, { 8047, 8039}, { 8120, 8112}, { 8121, 8113}, { 8122, 8048}, { 8123, 8049}, { 8136, 8050}, { 8137, 8051}, { 8138, 8052}, { 8139, 8053}, { 8152, 8144}, { 8153, 8145}, { 8154, 8054}, { 8155, 8055}, { 8168, 8160}, { 8169, 8161}, { 8170, 8058}, { 8171, 8059}, { 8172, 8165}, { 8184, 8056}, { 8185, 8057}, { 8186, 8060}, { 8187, 8061}, { 8544, 8560}, { 8545, 8561}, { 8546, 8562}, { 8547, 8563}, { 8548, 8564}, { 8549, 8565}, { 8550, 8566}, { 8551, 8567}, { 8552, 8568}, { 8553, 8569}, { 8554, 8570}, { 8555, 8571}, { 8556, 8572}, { 8557, 8573}, { 8558, 8574}, { 8559, 8575}, { 9398, 9424}, { 9399, 9425}, { 9400, 9426}, { 9401, 9427}, { 9402, 9428}, { 9403, 9429}, { 9404, 9430}, { 9405, 9431}, { 9406, 9432}, { 9407, 9433}, { 9408, 9434}, { 9409, 9435}, { 9410, 9436}, { 9411, 9437}, { 9412, 9438}, { 9413, 9439}, { 9414, 9440}, { 9415, 9441}, { 9416, 9442}, { 9417, 9443}, { 9418, 9444}, { 9419, 9445}, { 9420, 9446}, { 9421, 9447}, { 9422, 9448}, { 9423, 9449}, {65313,65345}, {65314,65346}, {65315,65347}, {65316,65348}, {65317,65349}, {65318,65350}, {65319,65351}, {65320,65352}, {65321,65353}, {65322,65354}, {65323,65355}, {65324,65356}, {65325,65357}, {65326,65358}, {65327,65359}, {65328,65360}, {65329,65361}, {65330,65362}, {65331,65363}, {65332,65364}, {65333,65365}, {65334,65366}, {65335,65367}, {65336,65368}, {65337,65369}, {65338,65370}, {65535, 0}}; int OVR_CDECL OVR_towupper(wchar_t charCode) { // Don't use UnicodeUpperBits! It differs from UnicodeToUpperBits. if (UnicodeCharIs(UnicodeToUpperBits, charCode)) { // To protect from memory overrun in case the character is not found // we use one extra fake element in the table {65536, 0}. UPInt idx = Alg::LowerBoundSliced( UnicodeToUpperTable, 0, sizeof(UnicodeToUpperTable) / sizeof(UnicodeToUpperTable[0]) - 1, (UInt16)charCode, CmpUnicodeKey); return UnicodeToUpperTable[idx].Value; } return charCode; } int OVR_CDECL OVR_towlower(wchar_t charCode) { // Don't use UnicodeLowerBits! It differs from UnicodeToLowerBits. if (UnicodeCharIs(UnicodeToLowerBits, charCode)) { // To protect from memory overrun in case the character is not found // we use one extra fake element in the table {65536, 0}. UPInt idx = Alg::LowerBoundSliced( UnicodeToLowerTable, 0, sizeof(UnicodeToLowerTable) / sizeof(UnicodeToLowerTable[0]) - 1, (UInt16)charCode, CmpUnicodeKey); return UnicodeToLowerTable[idx].Value; } return charCode; } #endif //OVR_NO_WCTYPE } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Std.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Std.h new file mode 100644 index 0000000..6d01b15 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Std.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Std.h Content : Standard C function interface Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Std_h #define OVR_Std_h #include "OVR_Types.h" #include // for va_list args #include #include #include #include #if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) #define OVR_MSVC_SAFESTRING #include #endif // Wide-char funcs #include #include namespace OVR { #if defined(OVR_OS_WIN32) inline char* OVR_CDECL OVR_itoa(int val, char *dest, UPInt destsize, int radix) { #if defined(OVR_MSVC_SAFESTRING) _itoa_s(val, dest, destsize, radix); return dest; #else OVR_UNUSED(destsize); return itoa(val, dest, radix); #endif } #else // OVR_OS_WIN32 inline char* OVR_itoa(int val, char* dest, unsigned int len, int radix) { if (val == 0) { if (len > 1) { dest[0] = '0'; dest[1] = '\0'; } return dest; } int cur = val; unsigned int i = 0; unsigned int sign = 0; if (val < 0) { val = -val; sign = 1; } while ((val != 0) && (i < (len - 1 - sign))) { cur = val % radix; val /= radix; if (radix == 16) { switch(cur) { case 10: dest[i] = 'a'; break; case 11: dest[i] = 'b'; break; case 12: dest[i] = 'c'; break; case 13: dest[i] = 'd'; break; case 14: dest[i] = 'e'; break; case 15: dest[i] = 'f'; break; default: dest[i] = (char)('0' + cur); break; } } else { dest[i] = (char)('0' + cur); } ++i; } if (sign) { dest[i++] = '-'; } for (unsigned int j = 0; j < i / 2; ++j) { char tmp = dest[j]; dest[j] = dest[i - 1 - j]; dest[i - 1 - j] = tmp; } dest[i] = '\0'; return dest; } #endif // String functions inline UPInt OVR_CDECL OVR_strlen(const char* str) { return strlen(str); } inline char* OVR_CDECL OVR_strcpy(char* dest, UPInt destsize, const char* src) { #if defined(OVR_MSVC_SAFESTRING) strcpy_s(dest, destsize, src); return dest; #else OVR_UNUSED(destsize); return strcpy(dest, src); #endif } inline char* OVR_CDECL OVR_strncpy(char* dest, UPInt destsize, const char* src, UPInt count) { #if defined(OVR_MSVC_SAFESTRING) strncpy_s(dest, destsize, src, count); return dest; #else OVR_UNUSED(destsize); return strncpy(dest, src, count); #endif } inline char * OVR_CDECL OVR_strcat(char* dest, UPInt destsize, const char* src) { #if defined(OVR_MSVC_SAFESTRING) strcat_s(dest, destsize, src); return dest; #else OVR_UNUSED(destsize); return strcat(dest, src); #endif } inline int OVR_CDECL OVR_strcmp(const char* dest, const char* src) { return strcmp(dest, src); } inline const char* OVR_CDECL OVR_strchr(const char* str, char c) { return strchr(str, c); } inline char* OVR_CDECL OVR_strchr(char* str, char c) { return strchr(str, c); } inline const char* OVR_strrchr(const char* str, char c) { UPInt len = OVR_strlen(str); for (UPInt i=len; i>0; i--) if (str[i]==c) return str+i; return 0; } inline const UByte* OVR_CDECL OVR_memrchr(const UByte* str, UPInt size, UByte c) { for (SPInt i = (SPInt)size - 1; i >= 0; i--) { if (str[i] == c) return str + i; } return 0; } inline char* OVR_CDECL OVR_strrchr(char* str, char c) { UPInt len = OVR_strlen(str); for (UPInt i=len; i>0; i--) if (str[i]==c) return str+i; return 0; } double OVR_CDECL OVR_strtod(const char* string, char** tailptr); inline long OVR_CDECL OVR_strtol(const char* string, char** tailptr, int radix) { return strtol(string, tailptr, radix); } inline long OVR_CDECL OVR_strtoul(const char* string, char** tailptr, int radix) { return strtoul(string, tailptr, radix); } inline int OVR_CDECL OVR_strncmp(const char* ws1, const char* ws2, UPInt size) { return strncmp(ws1, ws2, size); } inline UInt64 OVR_CDECL OVR_strtouq(const char *nptr, char **endptr, int base) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _strtoui64(nptr, endptr, base); #else return strtoull(nptr, endptr, base); #endif } inline SInt64 OVR_CDECL OVR_strtoq(const char *nptr, char **endptr, int base) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _strtoi64(nptr, endptr, base); #else return strtoll(nptr, endptr, base); #endif } inline SInt64 OVR_CDECL OVR_atoq(const char* string) { #if defined(OVR_CC_MSVC) && !defined(OVR_OS_WINCE) return _atoi64(string); #else return atoll(string); #endif } inline UInt64 OVR_CDECL OVR_atouq(const char* string) { return OVR_strtouq(string, NULL, 10); } // Implemented in GStd.cpp in platform-specific manner. int OVR_CDECL OVR_stricmp(const char* dest, const char* src); int OVR_CDECL OVR_strnicmp(const char* dest, const char* src, UPInt count); inline UPInt OVR_CDECL OVR_sprintf(char *dest, UPInt destsize, const char* format, ...) { va_list argList; va_start(argList,format); UPInt ret; #if defined(OVR_CC_MSVC) #if defined(OVR_MSVC_SAFESTRING) ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); OVR_ASSERT(ret != -1); #else OVR_UNUSED(destsize); ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character OVR_ASSERT(ret != -1); dest[destsize-1] = 0; #endif #else OVR_UNUSED(destsize); ret = vsprintf(dest, format, argList); OVR_ASSERT(ret < destsize); #endif va_end(argList); return ret; } inline UPInt OVR_CDECL OVR_vsprintf(char *dest, UPInt destsize, const char * format, va_list argList) { UPInt ret; #if defined(OVR_CC_MSVC) #if defined(OVR_MSVC_SAFESTRING) dest[0] = '\0'; int rv = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); if (rv == -1) { dest[destsize - 1] = '\0'; ret = destsize - 1; } else ret = (UPInt)rv; #else OVR_UNUSED(destsize); int rv = _vsnprintf(dest, destsize - 1, format, argList); OVR_ASSERT(rv != -1); ret = (UPInt)rv; dest[destsize-1] = 0; #endif #else OVR_UNUSED(destsize); ret = (UPInt)vsprintf(dest, format, argList); OVR_ASSERT(ret < destsize); #endif return ret; } // Returns the number of characters in the formatted string. inline UPInt OVR_CDECL OVR_vscprintf(const char * format, va_list argList) { UPInt ret; #if defined(OVR_CC_MSVC) ret = (UPInt) _vscprintf(format, argList); #else ret = (UPInt) vsnprintf(NULL, 0, format, argList); #endif return ret; } wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, UPInt destsize, const wchar_t* src); wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, UPInt destsize, const wchar_t* src, UPInt count); wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, UPInt destsize, const wchar_t* src); UPInt OVR_CDECL OVR_wcslen(const wchar_t* str); int OVR_CDECL OVR_wcscmp(const wchar_t* a, const wchar_t* b); int OVR_CDECL OVR_wcsicmp(const wchar_t* a, const wchar_t* b); inline int OVR_CDECL OVR_wcsicoll(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) #if defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) return ::_wcsicoll(a, b); #else return ::wcsicoll(a, b); #endif #else // not supported, use regular wcsicmp return OVR_wcsicmp(a, b); #endif } inline int OVR_CDECL OVR_wcscoll(const wchar_t* a, const wchar_t* b) { #if defined(OVR_OS_WIN32) || defined(OVR_OS_LINUX) return wcscoll(a, b); #else // not supported, use regular wcscmp return OVR_wcscmp(a, b); #endif } #ifndef OVR_NO_WCTYPE inline int OVR_CDECL UnicodeCharIs(const UInt16* table, wchar_t charCode) { unsigned offset = table[charCode >> 8]; if (offset == 0) return 0; if (offset == 1) return 1; return (table[offset + ((charCode >> 4) & 15)] & (1 << (charCode & 15))) != 0; } extern const UInt16 UnicodeAlnumBits[]; extern const UInt16 UnicodeAlphaBits[]; extern const UInt16 UnicodeDigitBits[]; extern const UInt16 UnicodeSpaceBits[]; extern const UInt16 UnicodeXDigitBits[]; // Uncomment if necessary //extern const UInt16 UnicodeCntrlBits[]; //extern const UInt16 UnicodeGraphBits[]; //extern const UInt16 UnicodeLowerBits[]; //extern const UInt16 UnicodePrintBits[]; //extern const UInt16 UnicodePunctBits[]; //extern const UInt16 UnicodeUpperBits[]; inline int OVR_CDECL OVR_iswalnum (wchar_t charCode) { return UnicodeCharIs(UnicodeAlnumBits, charCode); } inline int OVR_CDECL OVR_iswalpha (wchar_t charCode) { return UnicodeCharIs(UnicodeAlphaBits, charCode); } inline int OVR_CDECL OVR_iswdigit (wchar_t charCode) { return UnicodeCharIs(UnicodeDigitBits, charCode); } inline int OVR_CDECL OVR_iswspace (wchar_t charCode) { return UnicodeCharIs(UnicodeSpaceBits, charCode); } inline int OVR_CDECL OVR_iswxdigit(wchar_t charCode) { return UnicodeCharIs(UnicodeXDigitBits, charCode); } // Uncomment if necessary //inline int OVR_CDECL OVR_iswcntrl (wchar_t charCode) { return UnicodeCharIs(UnicodeCntrlBits, charCode); } //inline int OVR_CDECL OVR_iswgraph (wchar_t charCode) { return UnicodeCharIs(UnicodeGraphBits, charCode); } //inline int OVR_CDECL OVR_iswlower (wchar_t charCode) { return UnicodeCharIs(UnicodeLowerBits, charCode); } //inline int OVR_CDECL OVR_iswprint (wchar_t charCode) { return UnicodeCharIs(UnicodePrintBits, charCode); } //inline int OVR_CDECL OVR_iswpunct (wchar_t charCode) { return UnicodeCharIs(UnicodePunctBits, charCode); } //inline int OVR_CDECL OVR_iswupper (wchar_t charCode) { return UnicodeCharIs(UnicodeUpperBits, charCode); } int OVR_CDECL OVR_towupper(wchar_t charCode); int OVR_CDECL OVR_towlower(wchar_t charCode); #else // OVR_NO_WCTYPE inline int OVR_CDECL OVR_iswspace(wchar_t c) { return iswspace(c); } inline int OVR_CDECL OVR_iswdigit(wchar_t c) { return iswdigit(c); } inline int OVR_CDECL OVR_iswxdigit(wchar_t c) { return iswxdigit(c); } inline int OVR_CDECL OVR_iswalpha(wchar_t c) { return iswalpha(c); } inline int OVR_CDECL OVR_iswalnum(wchar_t c) { return iswalnum(c); } inline wchar_t OVR_CDECL OVR_towlower(wchar_t c) { return (wchar_t)towlower(c); } inline wchar_t OVR_towupper(wchar_t c) { return (wchar_t)towupper(c); } #endif // OVR_NO_WCTYPE // ASCII versions of tolower and toupper. Don't use "char" inline int OVR_CDECL OVR_tolower(int c) { return (c >= 'A' && c <= 'Z') ? c - 'A' + 'a' : c; } inline int OVR_CDECL OVR_toupper(int c) { return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; } inline double OVR_CDECL OVR_wcstod(const wchar_t* string, wchar_t** tailptr) { #if defined(OVR_OS_OTHER) OVR_UNUSED(tailptr); char buffer[64]; char* tp = NULL; UPInt max = OVR_wcslen(string); if (max > 63) max = 63; unsigned char c = 0; for (UPInt i=0; i < max; i++) { c = (unsigned char)string[i]; buffer[i] = ((c) < 128 ? (char)c : '!'); } buffer[max] = 0; return OVR_strtod(buffer, &tp); #else return wcstod(string, tailptr); #endif } inline long OVR_CDECL OVR_wcstol(const wchar_t* string, wchar_t** tailptr, int radix) { #if defined(OVR_OS_OTHER) OVR_UNUSED(tailptr); char buffer[64]; char* tp = NULL; UPInt max = OVR_wcslen(string); if (max > 63) max = 63; unsigned char c = 0; for (UPInt i=0; i < max; i++) { c = (unsigned char)string[i]; buffer[i] = ((c) < 128 ? (char)c : '!'); } buffer[max] = 0; return strtol(buffer, &tp, radix); #else return wcstol(string, tailptr, radix); #endif } } // OVR #endif // OVR_Std_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String.cpp new file mode 100644 index 0000000..539f95f --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_String.cpp Content : String UTF8 string implementation with copy-on-write semantics (thread-safe for assignment but not modification). Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_String.h" #include #include #ifdef OVR_OS_QNX # include #endif namespace OVR { #define String_LengthIsSize (UPInt(1) << String::Flag_LengthIsSizeShift) String::DataDesc String::NullData = {String_LengthIsSize, 1, {0} }; String::String() { pData = &NullData; pData->AddRef(); }; String::String(const char* pdata) { // Obtain length in bytes; it doesn't matter if _data is UTF8. UPInt size = pdata ? OVR_strlen(pdata) : 0; pData = AllocDataCopy1(size, 0, pdata, size); }; String::String(const char* pdata1, const char* pdata2, const char* pdata3) { // Obtain length in bytes; it doesn't matter if _data is UTF8. UPInt size1 = pdata1 ? OVR_strlen(pdata1) : 0; UPInt size2 = pdata2 ? OVR_strlen(pdata2) : 0; UPInt size3 = pdata3 ? OVR_strlen(pdata3) : 0; DataDesc *pdataDesc = AllocDataCopy2(size1 + size2 + size3, 0, pdata1, size1, pdata2, size2); memcpy(pdataDesc->Data + size1 + size2, pdata3, size3); pData = pdataDesc; } String::String(const char* pdata, UPInt size) { OVR_ASSERT((size == 0) || (pdata != 0)); pData = AllocDataCopy1(size, 0, pdata, size); }; String::String(const InitStruct& src, UPInt size) { pData = AllocData(size, 0); src.InitString(GetData()->Data, size); } String::String(const String& src) { pData = src.GetData(); pData->AddRef(); } String::String(const StringBuffer& src) { pData = AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize()); } String::String(const wchar_t* data) { pData = &NullData; pData->AddRef(); // Simplified logic for wchar_t constructor. if (data) *this = data; } String::DataDesc* String::AllocData(UPInt size, UPInt lengthIsSize) { String::DataDesc* pdesc; if (size == 0) { pdesc = &NullData; pdesc->AddRef(); return pdesc; } pdesc = (DataDesc*)OVR_ALLOC(sizeof(DataDesc)+ size); pdesc->Data[size] = 0; pdesc->RefCount = 1; pdesc->Size = size | lengthIsSize; return pdesc; } String::DataDesc* String::AllocDataCopy1(UPInt size, UPInt lengthIsSize, const char* pdata, UPInt copySize) { String::DataDesc* pdesc = AllocData(size, lengthIsSize); memcpy(pdesc->Data, pdata, copySize); return pdesc; } String::DataDesc* String::AllocDataCopy2(UPInt size, UPInt lengthIsSize, const char* pdata1, UPInt copySize1, const char* pdata2, UPInt copySize2) { String::DataDesc* pdesc = AllocData(size, lengthIsSize); memcpy(pdesc->Data, pdata1, copySize1); memcpy(pdesc->Data + copySize1, pdata2, copySize2); return pdesc; } UPInt String::GetLength() const { // Optimize length accesses for non-UTF8 character strings. DataDesc* pdata = GetData(); UPInt length, size = pdata->GetSize(); if (pdata->LengthIsSize()) return size; length = (UPInt)UTF8Util::GetLength(pdata->Data, (UPInt)size); if (length == size) pdata->Size |= String_LengthIsSize; return length; } //static UInt32 String_CharSearch(const char* buf, ) UInt32 String::GetCharAt(UPInt index) const { SPInt i = (SPInt) index; DataDesc* pdata = GetData(); const char* buf = pdata->Data; UInt32 c; if (pdata->LengthIsSize()) { OVR_ASSERT(index < pdata->GetSize()); buf += i; return UTF8Util::DecodeNextChar_Advance0(&buf); } c = UTF8Util::GetCharAt(index, buf, pdata->GetSize()); return c; } UInt32 String::GetFirstCharAt(UPInt index, const char** offset) const { DataDesc* pdata = GetData(); SPInt i = (SPInt) index; const char* buf = pdata->Data; const char* end = buf + pdata->GetSize(); UInt32 c; do { c = UTF8Util::DecodeNextChar_Advance0(&buf); i--; if (buf >= end) { // We've hit the end of the string; don't go further. OVR_ASSERT(i == 0); return c; } } while (i >= 0); *offset = buf; return c; } UInt32 String::GetNextChar(const char** offset) const { return UTF8Util::DecodeNextChar(offset); } void String::AppendChar(UInt32 ch) { DataDesc* pdata = GetData(); UPInt size = pdata->GetSize(); char buff[8]; SPInt encodeSize = 0; // Converts ch into UTF8 string and fills it into buff. UTF8Util::EncodeChar(buff, &encodeSize, ch); OVR_ASSERT(encodeSize >= 0); SetData(AllocDataCopy2(size + (UPInt)encodeSize, 0, pdata->Data, size, buff, (UPInt)encodeSize)); pdata->Release(); } void String::AppendString(const wchar_t* pstr, SPInt len) { if (!pstr) return; DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); UPInt encodeSize = (UPInt)UTF8Util::GetEncodeStringSize(pstr, len); DataDesc* pnewData = AllocDataCopy1(oldSize + (UPInt)encodeSize, 0, pdata->Data, oldSize); UTF8Util::EncodeString(pnewData->Data + oldSize, pstr, len); SetData(pnewData); pdata->Release(); } void String::AppendString(const char* putf8str, SPInt utf8StrSz) { if (!putf8str || !utf8StrSz) return; if (utf8StrSz == -1) utf8StrSz = (SPInt)OVR_strlen(putf8str); DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); SetData(AllocDataCopy2(oldSize + (UPInt)utf8StrSz, 0, pdata->Data, oldSize, putf8str, (UPInt)utf8StrSz)); pdata->Release(); } void String::AssignString(const InitStruct& src, UPInt size) { DataDesc* poldData = GetData(); DataDesc* pnewData = AllocData(size, 0); src.InitString(pnewData->Data, size); SetData(pnewData); poldData->Release(); } void String::AssignString(const char* putf8str, UPInt size) { DataDesc* poldData = GetData(); SetData(AllocDataCopy1(size, 0, putf8str, size)); poldData->Release(); } void String::operator = (const char* pstr) { AssignString(pstr, pstr ? OVR_strlen(pstr) : 0); } void String::operator = (const wchar_t* pwstr) { DataDesc* poldData = GetData(); UPInt size = pwstr ? (UPInt)UTF8Util::GetEncodeStringSize(pwstr) : 0; DataDesc* pnewData = AllocData(size, 0); UTF8Util::EncodeString(pnewData->Data, pwstr); SetData(pnewData); poldData->Release(); } void String::operator = (const String& src) { DataDesc* psdata = src.GetData(); DataDesc* pdata = GetData(); SetData(psdata); psdata->AddRef(); pdata->Release(); } void String::operator = (const StringBuffer& src) { DataDesc* polddata = GetData(); SetData(AllocDataCopy1(src.GetSize(), 0, src.ToCStr(), src.GetSize())); polddata->Release(); } void String::operator += (const String& src) { DataDesc *pourData = GetData(), *psrcData = src.GetData(); UPInt ourSize = pourData->GetSize(), srcSize = psrcData->GetSize(); UPInt lflag = pourData->GetLengthFlag() & psrcData->GetLengthFlag(); SetData(AllocDataCopy2(ourSize + srcSize, lflag, pourData->Data, ourSize, psrcData->Data, srcSize)); pourData->Release(); } String String::operator + (const char* str) const { String tmp1(*this); tmp1 += (str ? str : ""); return tmp1; } String String::operator + (const String& src) const { String tmp1(*this); tmp1 += src; return tmp1; } void String::Remove(UPInt posAt, SPInt removeLength) { DataDesc* pdata = GetData(); UPInt oldSize = pdata->GetSize(); // Length indicates the number of characters to remove. UPInt length = GetLength(); // If index is past the string, nothing to remove. if (posAt >= length) return; // Otherwise, cap removeLength to the length of the string. if ((posAt + removeLength) > length) removeLength = length - posAt; // Get the byte position of the UTF8 char at position posAt. SPInt bytePos = UTF8Util::GetByteIndex(posAt, pdata->Data, oldSize); SPInt removeSize = UTF8Util::GetByteIndex(removeLength, pdata->Data + bytePos, oldSize-bytePos); SetData(AllocDataCopy2(oldSize - removeSize, pdata->GetLengthFlag(), pdata->Data, bytePos, pData->Data + bytePos + removeSize, (oldSize - bytePos - removeSize))); pdata->Release(); } String String::Substring(UPInt start, UPInt end) const { UPInt length = GetLength(); if ((start >= length) || (start >= end)) return String(); DataDesc* pdata = GetData(); // If size matches, we know the exact index range. if (pdata->LengthIsSize()) return String(pdata->Data + start, end - start); // Get position of starting character. SPInt byteStart = UTF8Util::GetByteIndex(start, pdata->Data, pdata->GetSize()); SPInt byteSize = UTF8Util::GetByteIndex(end - start, pdata->Data + byteStart, pdata->GetSize()-byteStart); return String(pdata->Data + byteStart, (UPInt)byteSize); } void String::Clear() { NullData.AddRef(); GetData()->Release(); SetData(&NullData); } String String::ToUpper() const { UInt32 c; const char* psource = GetData()->Data; const char* pend = psource + GetData()->GetSize(); String str; SPInt bufferOffset = 0; char buffer[512]; while(psource < pend) { do { c = UTF8Util::DecodeNextChar_Advance0(&psource); UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towupper(wchar_t(c))); } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); // Append string a piece at a time. str.AppendString(buffer, bufferOffset); bufferOffset = 0; } return str; } String String::ToLower() const { UInt32 c; const char* psource = GetData()->Data; const char* pend = psource + GetData()->GetSize(); String str; SPInt bufferOffset = 0; char buffer[512]; while(psource < pend) { do { c = UTF8Util::DecodeNextChar_Advance0(&psource); UTF8Util::EncodeChar(buffer, &bufferOffset, OVR_towlower(wchar_t(c))); } while ((psource < pend) && (bufferOffset < SPInt(sizeof(buffer)-8))); // Append string a piece at a time. str.AppendString(buffer, bufferOffset); bufferOffset = 0; } return str; } String& String::Insert(const char* substr, UPInt posAt, SPInt strSize) { DataDesc* poldData = GetData(); UPInt oldSize = poldData->GetSize(); UPInt insertSize = (strSize < 0) ? OVR_strlen(substr) : (UPInt)strSize; UPInt byteIndex = (poldData->LengthIsSize()) ? posAt : (UPInt)UTF8Util::GetByteIndex(posAt, poldData->Data, oldSize); OVR_ASSERT(byteIndex <= oldSize); DataDesc* pnewData = AllocDataCopy2(oldSize + insertSize, 0, poldData->Data, byteIndex, substr, insertSize); memcpy(pnewData->Data + byteIndex + insertSize, poldData->Data + byteIndex, oldSize - byteIndex); SetData(pnewData); poldData->Release(); return *this; } /* String& String::Insert(const UInt32* substr, UPInt posAt, SPInt len) { for (SPInt i = 0; i < len; ++i) { UPInt charw = InsertCharAt(substr[i], posAt); posAt += charw; } return *this; } */ UPInt String::InsertCharAt(UInt32 c, UPInt posAt) { char buf[8]; SPInt index = 0; UTF8Util::EncodeChar(buf, &index, c); OVR_ASSERT(index >= 0); buf[(UPInt)index] = 0; Insert(buf, posAt, index); return (UPInt)index; } int String::CompareNoCase(const char* a, const char* b) { return OVR_stricmp(a, b); } int String::CompareNoCase(const char* a, const char* b, SPInt len) { if (len) { SPInt f,l; SPInt slen = len; const char *s = b; do { f = (SPInt)OVR_tolower((int)(*(a++))); l = (SPInt)OVR_tolower((int)(*(b++))); } while (--len && f && (f == l) && *b != 0); if (f == l && (len != 0 || *b != 0)) { f = (SPInt)slen; l = (SPInt)OVR_strlen(s); return int(f - l); } return int(f - l); } else return (0-(int)OVR_strlen(b)); } // ***** Implement hash static functions // Hash function UPInt String::BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed) { const UByte* pdata = (const UByte*) pdataIn; UPInt h = seed; while (size > 0) { size--; h = ((h << 5) + h) ^ (unsigned) pdata[size]; } return h; } // Hash function, case-insensitive UPInt String::BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed) { const UByte* pdata = (const UByte*) pdataIn; UPInt h = seed; while (size > 0) { size--; h = ((h << 5) + h) ^ OVR_tolower(pdata[size]); } // Alternative: "sdbm" hash function, suggested at same web page above. // h = 0; // for bytes { h = (h << 16) + (h << 6) - hash + *p; } return h; } // ***** String Buffer used for Building Strings #define OVR_SBUFF_DEFAULT_GROW_SIZE 512 // Constructors / Destructor. StringBuffer::StringBuffer() : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { } StringBuffer::StringBuffer(UPInt growSize) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { SetGrowSize(growSize); } StringBuffer::StringBuffer(const char* data) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(data); } StringBuffer::StringBuffer(const char* data, UPInt dataSize) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(data, dataSize); } StringBuffer::StringBuffer(const String& src) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(src.ToCStr(), src.GetSize()); } StringBuffer::StringBuffer(const StringBuffer& src) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { AppendString(src.ToCStr(), src.GetSize()); } StringBuffer::StringBuffer(const wchar_t* data) : pData(NULL), Size(0), BufferSize(0), GrowSize(OVR_SBUFF_DEFAULT_GROW_SIZE), LengthIsSize(false) { *this = data; } StringBuffer::~StringBuffer() { if (pData) OVR_FREE(pData); } void StringBuffer::SetGrowSize(UPInt growSize) { if (growSize <= 16) GrowSize = 16; else { UByte bits = Alg::UpperBit(UInt32(growSize-1)); UPInt size = 1<= BufferSize) // >= because of trailing zero! (!AB) { BufferSize = (_size + 1 + GrowSize - 1)& ~(GrowSize-1); if (!pData) pData = (char*)OVR_ALLOC(BufferSize); else pData = (char*)OVR_REALLOC(pData, BufferSize); } } void StringBuffer::Resize(UPInt _size) { Reserve(_size); LengthIsSize = false; Size = _size; if (pData) pData[Size] = 0; } void StringBuffer::Clear() { Resize(0); /* if (pData != pEmptyNullData) { OVR_FREE(pHeap, pData); pData = pEmptyNullData; Size = BufferSize = 0; LengthIsSize = false; } */ } // Appends a character void StringBuffer::AppendChar(UInt32 ch) { char buff[8]; UPInt origSize = GetSize(); // Converts ch into UTF8 string and fills it into buff. Also increments index according to the number of bytes // in the UTF8 string. SPInt srcSize = 0; UTF8Util::EncodeChar(buff, &srcSize, ch); OVR_ASSERT(srcSize >= 0); UPInt size = origSize + srcSize; Resize(size); memcpy(pData + origSize, buff, srcSize); } // Append a string void StringBuffer::AppendString(const wchar_t* pstr, SPInt len) { if (!pstr) return; SPInt srcSize = UTF8Util::GetEncodeStringSize(pstr, len); UPInt origSize = GetSize(); UPInt size = srcSize + origSize; Resize(size); UTF8Util::EncodeString(pData + origSize, pstr, len); } void StringBuffer::AppendString(const char* putf8str, SPInt utf8StrSz) { if (!putf8str || !utf8StrSz) return; if (utf8StrSz == -1) utf8StrSz = (SPInt)OVR_strlen(putf8str); UPInt origSize = GetSize(); UPInt size = utf8StrSz + origSize; Resize(size); memcpy(pData + origSize, putf8str, utf8StrSz); } void StringBuffer::operator = (const char* pstr) { pstr = pstr ? pstr : ""; UPInt size = OVR_strlen(pstr); Resize(size); memcpy(pData, pstr, size); } void StringBuffer::operator = (const wchar_t* pstr) { pstr = pstr ? pstr : L""; UPInt size = (UPInt)UTF8Util::GetEncodeStringSize(pstr); Resize(size); UTF8Util::EncodeString(pData, pstr); } void StringBuffer::operator = (const String& src) { Resize(src.GetSize()); memcpy(pData, src.ToCStr(), src.GetSize()); } void StringBuffer::operator = (const StringBuffer& src) { Clear(); AppendString(src.ToCStr(), src.GetSize()); } // Inserts substr at posAt void StringBuffer::Insert(const char* substr, UPInt posAt, SPInt len) { UPInt oldSize = Size; UPInt insertSize = (len < 0) ? OVR_strlen(substr) : (UPInt)len; UPInt byteIndex = LengthIsSize ? posAt : (UPInt)UTF8Util::GetByteIndex(posAt, pData, (SPInt)Size); OVR_ASSERT(byteIndex <= oldSize); Reserve(oldSize + insertSize); memmove(pData + byteIndex + insertSize, pData + byteIndex, oldSize - byteIndex + 1); memcpy (pData + byteIndex, substr, insertSize); LengthIsSize = false; Size = oldSize + insertSize; pData[Size] = 0; } // Inserts character at posAt UPInt StringBuffer::InsertCharAt(UInt32 c, UPInt posAt) { char buf[8]; SPInt len = 0; UTF8Util::EncodeChar(buf, &len, c); OVR_ASSERT(len >= 0); buf[(UPInt)len] = 0; Insert(buf, posAt, len); return (UPInt)len; } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String.h new file mode 100644 index 0000000..ea2deb8 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_String.h Content : String UTF8 string implementation with copy-on-write semantics (thread-safe for assignment but not modification). Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_String_h #define OVR_String_h #include "OVR_Types.h" #include "OVR_Allocator.h" #include "OVR_UTF8Util.h" #include "OVR_Atomic.h" #include "OVR_Std.h" #include "OVR_Alg.h" namespace OVR { // ***** Classes class String; class StringBuffer; //----------------------------------------------------------------------------------- // ***** String Class // String is UTF8 based string class with copy-on-write implementation // for assignment. class String { protected: enum FlagConstants { //Flag_GetLength = 0x7FFFFFFF, // This flag is set if GetLength() == GetSize() for a string. // Avoid extra scanning is Substring and indexing logic. Flag_LengthIsSizeShift = (sizeof(UPInt)*8 - 1) }; // Internal structure to hold string data struct DataDesc { // Number of bytes. Will be the same as the number of chars if the characters // are ascii, may not be equal to number of chars in case string data is UTF8. UPInt Size; volatile SInt32 RefCount; char Data[1]; void AddRef() { AtomicOps::ExchangeAdd_NoSync(&RefCount, 1); } // Decrement ref count. This needs to be thread-safe, since // a different thread could have also decremented the ref count. // For example, if u start off with a ref count = 2. Now if u // decrement the ref count and check against 0 in different // statements, a different thread can also decrement the ref count // in between our decrement and checking against 0 and will find // the ref count = 0 and delete the object. This will lead to a crash // when context switches to our thread and we'll be trying to delete // an already deleted object. Hence decrementing the ref count and // checking against 0 needs to made an atomic operation. void Release() { if ((AtomicOps::ExchangeAdd_NoSync(&RefCount, -1) - 1) == 0) OVR_FREE(this); } static UPInt GetLengthFlagBit() { return UPInt(1) << Flag_LengthIsSizeShift; } UPInt GetSize() const { return Size & ~GetLengthFlagBit() ; } UPInt GetLengthFlag() const { return Size & GetLengthFlagBit(); } bool LengthIsSize() const { return GetLengthFlag() != 0; } }; // Heap type of the string is encoded in the lower bits. enum HeapType { HT_Global = 0, // Heap is global. HT_Local = 1, // SF::String_loc: Heap is determined based on string's address. HT_Dynamic = 2, // SF::String_temp: Heap is stored as a part of the class. HT_Mask = 3 }; union { DataDesc* pData; UPInt HeapTypeBits; }; typedef union { DataDesc* pData; UPInt HeapTypeBits; } DataDescUnion; inline HeapType GetHeapType() const { return (HeapType) (HeapTypeBits & HT_Mask); } inline DataDesc* GetData() const { DataDescUnion u; u.pData = pData; u.HeapTypeBits = (u.HeapTypeBits & ~(UPInt)HT_Mask); return u.pData; } inline void SetData(DataDesc* pdesc) { HeapType ht = GetHeapType(); pData = pdesc; OVR_ASSERT((HeapTypeBits & HT_Mask) == 0); HeapTypeBits |= ht; } DataDesc* AllocData(UPInt size, UPInt lengthIsSize); DataDesc* AllocDataCopy1(UPInt size, UPInt lengthIsSize, const char* pdata, UPInt copySize); DataDesc* AllocDataCopy2(UPInt size, UPInt lengthIsSize, const char* pdata1, UPInt copySize1, const char* pdata2, UPInt copySize2); // Special constructor to avoid data initalization when used in derived class. struct NoConstructor { }; String(const NoConstructor&) { } public: // For initializing string with dynamic buffer struct InitStruct { virtual ~InitStruct() { } virtual void InitString(char* pbuffer, UPInt size) const = 0; }; // Constructors / Destructors. String(); String(const char* data); String(const char* data1, const char* pdata2, const char* pdata3 = 0); String(const char* data, UPInt buflen); String(const String& src); String(const StringBuffer& src); String(const InitStruct& src, UPInt size); explicit String(const wchar_t* data); // Destructor (Captain Obvious guarantees!) ~String() { GetData()->Release(); } // Declaration of NullString static DataDesc NullData; // *** General Functions void Clear(); // For casting to a pointer to char. operator const char*() const { return GetData()->Data; } // Pointer to raw buffer. const char* ToCStr() const { return GetData()->Data; } // Returns number of bytes UPInt GetSize() const { return GetData()->GetSize() ; } // Tells whether or not the string is empty bool IsEmpty() const { return GetSize() == 0; } // Returns number of characters UPInt GetLength() const; // Returns character at the specified index UInt32 GetCharAt(UPInt index) const; UInt32 GetFirstCharAt(UPInt index, const char** offset) const; UInt32 GetNextChar(const char** offset) const; // Appends a character void AppendChar(UInt32 ch); // Append a string void AppendString(const wchar_t* pstr, SPInt len = -1); void AppendString(const char* putf8str, SPInt utf8StrSz = -1); // Assigned a string with dynamic data (copied through initializer). void AssignString(const InitStruct& src, UPInt size); // Assigns string with known size. void AssignString(const char* putf8str, UPInt size); // Resize the string to the new size // void Resize(UPInt _size); // Removes the character at posAt void Remove(UPInt posAt, SPInt len = 1); // Returns a String that's a substring of this. // -start is the index of the first UTF8 character you want to include. // -end is the index one past the last UTF8 character you want to include. String Substring(UPInt start, UPInt end) const; // Case-conversion String ToUpper() const; String ToLower() const; // Inserts substr at posAt String& Insert (const char* substr, UPInt posAt, SPInt len = -1); // Inserts character at posAt UPInt InsertCharAt(UInt32 c, UPInt posAt); // Inserts substr at posAt, which is an index of a character (not byte). // Of size is specified, it is in bytes. // String& Insert(const UInt32* substr, UPInt posAt, SPInt size = -1); // Get Byte index of the character at position = index UPInt GetByteIndex(UPInt index) const { return (UPInt)UTF8Util::GetByteIndex(index, GetData()->Data); } // Utility: case-insensitive string compare. stricmp() & strnicmp() are not // ANSI or POSIX, do not seem to appear in Linux. static int OVR_STDCALL CompareNoCase(const char* a, const char* b); static int OVR_STDCALL CompareNoCase(const char* a, const char* b, SPInt len); // Hash function, case-insensitive static UPInt OVR_STDCALL BernsteinHashFunctionCIS(const void* pdataIn, UPInt size, UPInt seed = 5381); // Hash function, case-sensitive static UPInt OVR_STDCALL BernsteinHashFunction(const void* pdataIn, UPInt size, UPInt seed = 5381); // ***** File path parsing helper functions. // Implemented in OVR_String_FilePath.cpp. // Absolute paths can star with: // - protocols: 'file://', 'http://' // - windows drive: 'c:\' // - UNC share name: '\\share' // - unix root '/' static bool HasAbsolutePath(const char* path); static bool HasExtension(const char* path); static bool HasProtocol(const char* path); bool HasAbsolutePath() const { return HasAbsolutePath(ToCStr()); } bool HasExtension() const { return HasExtension(ToCStr()); } bool HasProtocol() const { return HasProtocol(ToCStr()); } String GetProtocol() const; // Returns protocol, if any, with trailing '://'. String GetPath() const; // Returns path with trailing '/'. String GetFilename() const; // Returns filename, including extension. String GetExtension() const; // Returns extension with a dot. void StripProtocol(); // Strips front protocol, if any, from the string. void StripExtension(); // Strips off trailing extension. // Operators // Assignment void operator = (const char* str); void operator = (const wchar_t* str); void operator = (const String& src); void operator = (const StringBuffer& src); // Addition void operator += (const String& src); void operator += (const char* psrc) { AppendString(psrc); } void operator += (const wchar_t* psrc) { AppendString(psrc); } void operator += (char ch) { AppendChar(ch); } String operator + (const char* str) const; String operator + (const String& src) const; // Comparison bool operator == (const String& str) const { return (OVR_strcmp(GetData()->Data, str.GetData()->Data)== 0); } bool operator != (const String& str) const { return !operator == (str); } bool operator == (const char* str) const { return OVR_strcmp(GetData()->Data, str) == 0; } bool operator != (const char* str) const { return !operator == (str); } bool operator < (const char* pstr) const { return OVR_strcmp(GetData()->Data, pstr) < 0; } bool operator < (const String& str) const { return *this < str.GetData()->Data; } bool operator > (const char* pstr) const { return OVR_strcmp(GetData()->Data, pstr) > 0; } bool operator > (const String& str) const { return *this > str.GetData()->Data; } int CompareNoCase(const char* pstr) const { return CompareNoCase(GetData()->Data, pstr); } int CompareNoCase(const String& str) const { return CompareNoCase(GetData()->Data, str.ToCStr()); } // Accesses raw bytes const char& operator [] (int index) const { OVR_ASSERT(index >= 0 && (UPInt)index < GetSize()); return GetData()->Data[index]; } const char& operator [] (UPInt index) const { OVR_ASSERT(index < GetSize()); return GetData()->Data[index]; } // Case insensitive keys are used to look up insensitive string in hash tables // for SWF files with version before SWF 7. struct NoCaseKey { const String* pStr; NoCaseKey(const String &str) : pStr(&str){}; }; bool operator == (const NoCaseKey& strKey) const { return (CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); } bool operator != (const NoCaseKey& strKey) const { return !(CompareNoCase(ToCStr(), strKey.pStr->ToCStr()) == 0); } // Hash functor used for strings. struct HashFunctor { UPInt operator()(const String& data) const { UPInt size = data.GetSize(); return String::BernsteinHashFunction((const char*)data, size); } }; // Case-insensitive hash functor used for strings. Supports additional // lookup based on NoCaseKey. struct NoCaseHashFunctor { UPInt operator()(const String& data) const { UPInt size = data.GetSize(); return String::BernsteinHashFunctionCIS((const char*)data, size); } UPInt operator()(const NoCaseKey& data) const { UPInt size = data.pStr->GetSize(); return String::BernsteinHashFunctionCIS((const char*)data.pStr->ToCStr(), size); } }; }; //----------------------------------------------------------------------------------- // ***** String Buffer used for Building Strings class StringBuffer { char* pData; UPInt Size; UPInt BufferSize; UPInt GrowSize; mutable bool LengthIsSize; public: // Constructors / Destructor. StringBuffer(); explicit StringBuffer(UPInt growSize); StringBuffer(const char* data); StringBuffer(const char* data, UPInt buflen); StringBuffer(const String& src); StringBuffer(const StringBuffer& src); explicit StringBuffer(const wchar_t* data); ~StringBuffer(); // Modify grow size used for growing/shrinking the buffer. UPInt GetGrowSize() const { return GrowSize; } void SetGrowSize(UPInt growSize); // *** General Functions // Does not release memory, just sets Size to 0 void Clear(); // For casting to a pointer to char. operator const char*() const { return (pData) ? pData : ""; } // Pointer to raw buffer. const char* ToCStr() const { return (pData) ? pData : ""; } // Returns number of bytes. UPInt GetSize() const { return Size ; } // Tells whether or not the string is empty. bool IsEmpty() const { return GetSize() == 0; } // Returns number of characters UPInt GetLength() const; // Returns character at the specified index UInt32 GetCharAt(UPInt index) const; UInt32 GetFirstCharAt(UPInt index, const char** offset) const; UInt32 GetNextChar(const char** offset) const; // Resize the string to the new size void Resize(UPInt _size); void Reserve(UPInt _size); // Appends a character void AppendChar(UInt32 ch); // Append a string void AppendString(const wchar_t* pstr, SPInt len = -1); void AppendString(const char* putf8str, SPInt utf8StrSz = -1); void AppendFormat(const char* format, ...); // Assigned a string with dynamic data (copied through initializer). //void AssignString(const InitStruct& src, UPInt size); // Inserts substr at posAt void Insert (const char* substr, UPInt posAt, SPInt len = -1); // Inserts character at posAt UPInt InsertCharAt(UInt32 c, UPInt posAt); // Assignment void operator = (const char* str); void operator = (const wchar_t* str); void operator = (const String& src); void operator = (const StringBuffer& src); // Addition void operator += (const String& src) { AppendString(src.ToCStr(),src.GetSize()); } void operator += (const char* psrc) { AppendString(psrc); } void operator += (const wchar_t* psrc) { AppendString(psrc); } void operator += (char ch) { AppendChar(ch); } //String operator + (const char* str) const ; //String operator + (const String& src) const ; // Accesses raw bytes char& operator [] (int index) { OVR_ASSERT(((UPInt)index) < GetSize()); return pData[index]; } char& operator [] (UPInt index) { OVR_ASSERT(index < GetSize()); return pData[index]; } const char& operator [] (int index) const { OVR_ASSERT(((UPInt)index) < GetSize()); return pData[index]; } const char& operator [] (UPInt index) const { OVR_ASSERT(index < GetSize()); return pData[index]; } }; // // Wrapper for string data. The data must have a guaranteed // lifespan throughout the usage of the wrapper. Not intended for // cached usage. Not thread safe. // class StringDataPtr { public: StringDataPtr() : pStr(NULL), Size(0) {} StringDataPtr(const StringDataPtr& p) : pStr(p.pStr), Size(p.Size) {} StringDataPtr(const char* pstr, UPInt sz) : pStr(pstr), Size(sz) {} StringDataPtr(const char* pstr) : pStr(pstr), Size((pstr != NULL) ? OVR_strlen(pstr) : 0) {} explicit StringDataPtr(const String& str) : pStr(str.ToCStr()), Size(str.GetSize()) {} template StringDataPtr(const T (&v)[N]) : pStr(v), Size(N) {} public: const char* ToCStr() const { return pStr; } UPInt GetSize() const { return Size; } bool IsEmpty() const { return GetSize() == 0; } // value is a prefix of this string // Character's values are not compared. bool IsPrefix(const StringDataPtr& value) const { return ToCStr() == value.ToCStr() && GetSize() >= value.GetSize(); } // value is a suffix of this string // Character's values are not compared. bool IsSuffix(const StringDataPtr& value) const { return ToCStr() <= value.ToCStr() && (End()) == (value.End()); } // Find first character. // init_ind - initial index. SPInt FindChar(char c, UPInt init_ind = 0) const { for (UPInt i = init_ind; i < GetSize(); ++i) if (pStr[i] == c) return static_cast(i); return -1; } // Find last character. // init_ind - initial index. SPInt FindLastChar(char c, UPInt init_ind = ~0) const { if (init_ind == (UPInt)~0 || init_ind > GetSize()) init_ind = GetSize(); else ++init_ind; for (UPInt i = init_ind; i > 0; --i) if (pStr[i - 1] == c) return static_cast(i - 1); return -1; } // Create new object and trim size bytes from the left. StringDataPtr GetTrimLeft(UPInt size) const { // Limit trim size to the size of the string. size = Alg::PMin(GetSize(), size); return StringDataPtr(ToCStr() + size, GetSize() - size); } // Create new object and trim size bytes from the right. StringDataPtr GetTrimRight(UPInt size) const { // Limit trim to the size of the string. size = Alg::PMin(GetSize(), size); return StringDataPtr(ToCStr(), GetSize() - size); } // Create new object, which contains next token. // Useful for parsing. StringDataPtr GetNextToken(char separator = ':') const { UPInt cur_pos = 0; const char* cur_str = ToCStr(); for (; cur_pos < GetSize() && cur_str[cur_pos]; ++cur_pos) { if (cur_str[cur_pos] == separator) { break; } } return StringDataPtr(ToCStr(), cur_pos); } // Trim size bytes from the left. StringDataPtr& TrimLeft(UPInt size) { // Limit trim size to the size of the string. size = Alg::PMin(GetSize(), size); pStr += size; Size -= size; return *this; } // Trim size bytes from the right. StringDataPtr& TrimRight(UPInt size) { // Limit trim to the size of the string. size = Alg::PMin(GetSize(), size); Size -= size; return *this; } const char* Begin() const { return ToCStr(); } const char* End() const { return ToCStr() + GetSize(); } // Hash functor used string data pointers struct HashFunctor { UPInt operator()(const StringDataPtr& data) const { return String::BernsteinHashFunction(data.ToCStr(), data.GetSize()); } }; bool operator== (const StringDataPtr& data) const { return (OVR_strncmp(pStr, data.pStr, data.Size) == 0); } protected: const char* pStr; UPInt Size; }; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_StringHash.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_StringHash.h new file mode 100644 index 0000000..a617bf6 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_StringHash.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_StringHash.h Content : String hash table used when optional case-insensitive lookup is required. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_StringHash_h #define OVR_StringHash_h #include "OVR_String.h" #include "OVR_Hash.h" namespace OVR { //----------------------------------------------------------------------------------- // *** StringHash // This is a custom string hash table that supports case-insensitive // searches through special functions such as GetCaseInsensitive, etc. // This class is used for Flash labels, exports and other case-insensitive tables. template > class StringHash : public Hash { public: typedef U ValueType; typedef StringHash SelfType; typedef Hash BaseType; public: void operator = (const SelfType& src) { BaseType::operator = (src); } bool GetCaseInsensitive(const String& key, U* pvalue) const { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey, pvalue); } // Pointer-returning get variety. const U* GetCaseInsensitive(const String& key) const { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey); } U* GetCaseInsensitive(const String& key) { String::NoCaseKey ikey(key); return BaseType::GetAlt(ikey); } typedef typename BaseType::Iterator base_iterator; base_iterator FindCaseInsensitive(const String& key) { String::NoCaseKey ikey(key); return BaseType::FindAlt(ikey); } // Set just uses a find and assigns value if found. The key is not modified; // this behavior is identical to Flash string variable assignment. void SetCaseInsensitive(const String& key, const U& value) { base_iterator it = FindCaseInsensitive(key); if (it != BaseType::End()) { it->Second = value; } else { BaseType::Add(key, value); } } }; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp new file mode 100644 index 0000000..29d0b87 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String_FormatUtil.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_String_FormatUtil.cpp Content : String format functions. Created : February 27, 2013 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_String.h" #include "OVR_Log.h" namespace OVR { void StringBuffer::AppendFormat(const char* format, ...) { va_list argList; va_start(argList, format); UPInt size = OVR_vscprintf(format, argList); va_end(argList); char* buffer = (char*) OVR_ALLOC(sizeof(char) * (size+1)); va_start(argList, format); UPInt result = OVR_vsprintf(buffer, size+1, format, argList); OVR_UNUSED1(result); va_end(argList); OVR_ASSERT_LOG(result == size, ("Error in OVR_vsprintf")); AppendString(buffer); OVR_FREE(buffer); } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp new file mode 100644 index 0000000..a32e6a7 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_String_PathUtil.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_String_PathUtil.cpp Content : String filename/url helper function Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_String.h" #include "OVR_UTF8Util.h" namespace OVR { //-------------------------------------------------------------------- // ***** Path-Scanner helper function // Scans file path finding filename start and extension start, fills in their addess. void ScanFilePath(const char* url, const char** pfilename, const char** pext) { const char* urlStart = url; const char *filename = 0; const char *lastDot = 0; UInt32 charVal = UTF8Util::DecodeNextChar(&url); while (charVal != 0) { if ((charVal == '/') || (charVal == '\\')) { filename = url; lastDot = 0; } else if (charVal == '.') { lastDot = url - 1; } charVal = UTF8Util::DecodeNextChar(&url); } if (pfilename) { // It was a naked filename if (urlStart && (*urlStart != '.') && *urlStart) *pfilename = urlStart; else *pfilename = filename; } if (pext) { *pext = lastDot; } } // Scans till the end of protocol. Returns first character past protocol, // 0 if not found. // - protocol: 'file://', 'http://' const char* ScanPathProtocol(const char* url) { UInt32 charVal = UTF8Util::DecodeNextChar(&url); UInt32 charVal2; while (charVal != 0) { // Treat a colon followed by a slash as absolute. if (charVal == ':') { charVal2 = UTF8Util::DecodeNextChar(&url); charVal = UTF8Util::DecodeNextChar(&url); if ((charVal == '/') && (charVal2 == '\\')) return url; } charVal = UTF8Util::DecodeNextChar(&url); } return 0; } //-------------------------------------------------------------------- // ***** String Path API implementation bool String::HasAbsolutePath(const char* url) { // Absolute paths can star with: // - protocols: 'file://', 'http://' // - windows drive: 'c:\' // - UNC share name: '\\share' // - unix root '/' // On the other hand, relative paths are: // - directory: 'directory/file' // - this directory: './file' // - parent directory: '../file' // // For now, we don't parse '.' or '..' out, but instead let it be concatenated // to string and let the OS figure it out. This, however, is not good for file // name matching in library/etc, so it should be improved. if (!url || !*url) return true; // Treat empty strings as absolute. UInt32 charVal = UTF8Util::DecodeNextChar(&url); // Fist character of '/' or '\\' means absolute url. if ((charVal == '/') || (charVal == '\\')) return true; while (charVal != 0) { // Treat a colon followed by a slash as absolute. if (charVal == ':') { charVal = UTF8Util::DecodeNextChar(&url); // Protocol or windows drive. Absolute. if ((charVal == '/') || (charVal == '\\')) return true; } else if ((charVal == '/') || (charVal == '\\')) { // Not a first character (else 'if' above the loop would have caught it). // Must be a relative url. break; } charVal = UTF8Util::DecodeNextChar(&url); } // We get here for relative paths. return false; } bool String::HasExtension(const char* path) { const char* ext = 0; ScanFilePath(path, 0, &ext); return ext != 0; } bool String::HasProtocol(const char* path) { return ScanPathProtocol(path) != 0; } String String::GetPath() const { const char* filename = 0; ScanFilePath(ToCStr(), &filename, 0); // Technically we can have extra logic somewhere for paths, // such as enforcing protocol and '/' only based on flags, // but we keep it simple for now. return String(ToCStr(), filename ? (filename-ToCStr()) : GetSize()); } String String::GetProtocol() const { const char* protocolEnd = ScanPathProtocol(ToCStr()); return String(ToCStr(), protocolEnd ? (protocolEnd-ToCStr()) : 0); } String String::GetFilename() const { const char* filename = 0; ScanFilePath(ToCStr(), &filename, 0); return String(filename); } String String::GetExtension() const { const char* ext = 0; ScanFilePath(ToCStr(), 0, &ext); return String(ext); } void String::StripExtension() { const char* ext = 0; ScanFilePath(ToCStr(), 0, &ext); if (ext) { *this = String(ToCStr(), ext-ToCStr()); } } void String::StripProtocol() { const char* protocol = ScanPathProtocol(ToCStr()); if (protocol) AssignString(protocol, OVR_strlen(protocol)); } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_SysFile.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_SysFile.cpp new file mode 100644 index 0000000..f971c49 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_SysFile.cpp @@ -0,0 +1 @@ +/************************************************************************** Filename : OVR_SysFile.cpp Content : File wrapper class implementation (Win32) Created : April 5, 1999 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. **************************************************************************/ #define GFILE_CXX // Standard C library (Captain Obvious guarantees!) #include #include "OVR_SysFile.h" #include "OVR_Log.h" namespace OVR { // This is - a dummy file that fails on all calls. class UnopenedFile : public File { public: UnopenedFile() { } ~UnopenedFile() { } virtual const char* GetFilePath() { return 0; } // ** File Information virtual bool IsValid() { return 0; } virtual bool IsWritable() { return 0; } // Return position / file size virtual int Tell() { return 0; } virtual SInt64 LTell() { return 0; } virtual int GetLength() { return 0; } virtual SInt64 LGetLength() { return 0; } // virtual bool Stat(FileStats *pfs) { return 0; } virtual int GetErrorCode() { return Error_FileNotFound; } // ** Stream implementation & I/O virtual int Write(const UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } virtual int Read(UByte *pbuffer, int numBytes) { return -1; OVR_UNUSED2(pbuffer, numBytes); } virtual int SkipBytes(int numBytes) { return 0; OVR_UNUSED(numBytes); } virtual int BytesAvailable() { return 0; } virtual bool Flush() { return 0; } virtual int Seek(int offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } virtual SInt64 LSeek(SInt64 offset, int origin) { return -1; OVR_UNUSED2(offset, origin); } virtual int CopyFromStream(File *pstream, int byteSize) { return -1; OVR_UNUSED2(pstream, byteSize); } virtual bool Close() { return 0; } }; // ***** System File // System file is created to access objects on file system directly // This file can refer directly to path // ** Constructor SysFile::SysFile() : DelegatedFile(0) { pFile = *new UnopenedFile; } Ptr FileFILEOpen(const String& path, int flags, int mode); // Opens a file SysFile::SysFile(const String& path, int flags, int mode) : DelegatedFile(0) { Open(path, flags, mode); } // ** Open & management // Will fail if file's already open bool SysFile::Open(const String& path, int flags, int mode) { pFile = FileFILEOpen(path, flags, mode); if ((!pFile) || (!pFile->IsValid())) { pFile = *new UnopenedFile; OVR_DEBUG_LOG(("Failed to open file: %s", path.ToCStr())); return 0; } //pFile = *OVR_NEW DelegatedFile(pFile); // MA Testing if (flags & Open_Buffered) pFile = *new BufferedFile(pFile); return 1; } // ** Overrides int SysFile::GetErrorCode() { return pFile ? pFile->GetErrorCode() : Error_FileNotFound; } // Overrides to provide re-open support bool SysFile::IsValid() { return pFile && pFile->IsValid(); } bool SysFile::Close() { if (IsValid()) { DelegatedFile::Close(); pFile = *new UnopenedFile; return 1; } return 0; } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_SysFile.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_SysFile.h new file mode 100644 index 0000000..971772b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_SysFile.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: Kernel Filename : OVR_SysFile.h Content : Header for all internal file management - functions and structures to be inherited by OS specific subclasses. Created : September 19, 2012 Notes : Notes : errno may not be preserved across use of GBaseFile member functions : Directories cannot be deleted while files opened from them are in use (For the GetFullName function) Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_SysFile_h #define OVR_SysFile_h #include "OVR_File.h" namespace OVR { // ***** Declared classes class SysFile; //----------------------------------------------------------------------------------- // *** File Statistics // This class contents are similar to _stat, providing // creation, modify and other information about the file. struct FileStat { // No change or create time because they are not available on most systems SInt64 ModifyTime; SInt64 AccessTime; SInt64 FileSize; bool operator== (const FileStat& stat) const { return ( (ModifyTime == stat.ModifyTime) && (AccessTime == stat.AccessTime) && (FileSize == stat.FileSize) ); } }; //----------------------------------------------------------------------------------- // *** System File // System file is created to access objects on file system directly // This file can refer directly to path. // System file can be open & closed several times; however, such use is not recommended // This class is realy a wrapper around an implementation of File interface for a // particular platform. class SysFile : public DelegatedFile { protected: SysFile(const SysFile &source) : DelegatedFile () { OVR_UNUSED(source); } public: // ** Constructor SysFile(); // Opens a file SysFile(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); // ** Open & management bool Open(const String& path, int flags = Open_Read|Open_Buffered, int mode = Mode_ReadWrite); OVR_FORCE_INLINE bool Create(const String& path, int mode = Mode_ReadWrite) { return Open(path, Open_ReadWrite|Open_Create, mode); } // Helper function: obtain file statistics information. In OVR, this is used to detect file changes. // Return 0 if function failed, most likely because the file doesn't exist. static bool OVR_CDECL GetFileStat(FileStat* pfileStats, const String& path); // ** Overrides // Overridden to provide re-open support virtual int GetErrorCode(); virtual bool IsValid(); virtual bool Close(); }; } // Namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_System.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_System.cpp new file mode 100644 index 0000000..9397d7d --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_System.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_System.cpp Content : General kernel initialization/cleanup, including that of the memory allocator. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_System.h" #include "OVR_Threads.h" #include "OVR_Timer.h" namespace OVR { // ***** OVR::System Implementation // Initializes System core, installing allocator. void System::Init(Log* log, Allocator *palloc) { if (!Allocator::GetInstance()) { Log::SetGlobalLog(log); Timer::initializeTimerSystem(); Allocator::setInstance(palloc); } else { OVR_DEBUG_LOG(("System::Init failed - duplicate call.")); } } void System::Destroy() { if (Allocator::GetInstance()) { // Wait for all threads to finish; this must be done so that memory // allocator and all destructors finalize correctly. #ifdef OVR_ENABLE_THREADS Thread::FinishAllThreads(); #endif // Shutdown heap and destroy SysAlloc singleton, if any. Allocator::GetInstance()->onSystemShutdown(); Allocator::setInstance(0); Timer::shutdownTimerSystem(); Log::SetGlobalLog(Log::GetDefaultLog()); } else { OVR_DEBUG_LOG(("System::Destroy failed - System not initialized.")); } } // Returns 'true' if system was properly initialized. bool System::IsInitialized() { return Allocator::GetInstance() != 0; } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_System.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_System.h new file mode 100644 index 0000000..39253ee --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_System.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_System.h Content : General kernel initialization/cleanup, including that of the memory allocator. Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_System_h #define OVR_System_h #include "OVR_Allocator.h" #include "OVR_Log.h" namespace OVR { // ***** System Core Initialization class // System initialization must take place before any other OVR_Kernel objects are used; // this is done my calling System::Init(). Among other things, this is necessary to // initialize the memory allocator. Similarly, System::Destroy must be // called before program exist for proper cleanup. Both of these tasks can be achieved by // simply creating System object first, allowing its constructor/destructor do the work. // TBD: Require additional System class for Oculus Rift API? class System { public: // System constructor expects allocator to be specified, if it is being substituted. System(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), Allocator* palloc = DefaultAllocator::InitSystemSingleton()) { Init(log, palloc); } ~System() { Destroy(); } // Returns 'true' if system was properly initialized. static bool OVR_CDECL IsInitialized(); // Initializes System core. Users can override memory implementation by passing // a different Allocator here. static void OVR_CDECL Init(Log* log = Log::ConfigureDefaultLog(LogMask_Debug), Allocator *palloc = DefaultAllocator::InitSystemSingleton()); // De-initializes System more, finalizing the threading system and destroying // the global memory allocator. static void OVR_CDECL Destroy(); }; } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Threads.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Threads.h new file mode 100644 index 0000000..ddb852b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Threads.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_Threads.h Content : Contains thread-related (safe) functionality Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Threads_h #define OVR_Threads_h #include "OVR_Types.h" #include "OVR_Atomic.h" #include "OVR_RefCount.h" #include "OVR_Array.h" // Defines the infinite wait delay timeout #define OVR_WAIT_INFINITE 0xFFFFFFFF // To be defined in the project configuration options #ifdef OVR_ENABLE_THREADS namespace OVR { //----------------------------------------------------------------------------------- // ****** Declared classes // Declared with thread support only class Mutex; class WaitCondition; class Event; // Implementation forward declarations class MutexImpl; class WaitConditionImpl; //----------------------------------------------------------------------------------- // ***** Mutex // Mutex class represents a system Mutex synchronization object that provides access // serialization between different threads, allowing one thread mutually exclusive access // to a resource. Mutex is more heavy-weight then Lock, but supports WaitCondition. class Mutex { friend class WaitConditionImpl; friend class MutexImpl; MutexImpl *pImpl; public: // Constructor/destructor Mutex(bool recursive = 1); ~Mutex(); // Locking functions void DoLock(); bool TryLock(); void Unlock(); // Returns 1 if the mutes is currently locked by another thread // Returns 0 if the mutex is not locked by another thread, and can therefore be acquired. bool IsLockedByAnotherThread(); // Locker class; Used for automatic locking of a mutex withing scope class Locker { public: Mutex *pMutex; Locker(Mutex *pmutex) { pMutex = pmutex; pMutex->DoLock(); } ~Locker() { pMutex->Unlock(); } }; }; //----------------------------------------------------------------------------------- // ***** WaitCondition /* WaitCondition is a synchronization primitive that can be used to implement what is known as a monitor. Dependent threads wait on a wait condition by calling Wait(), and get woken up by other threads that call Notify() or NotifyAll(). The unique feature of this class is that it provides an atomic way of first releasing a Mutex, and then starting a wait on a wait condition. If both the mutex and the wait condition are associated with the same resource, this ensures that any condition checked for while the mutex was locked does not change before the wait on the condition is actually initiated. */ class WaitCondition { friend class WaitConditionImpl; // Internal implementation structure WaitConditionImpl *pImpl; public: // Constructor/destructor WaitCondition(); ~WaitCondition(); // Release mutex and wait for condition. The mutex is re-aquired after the wait. // Delay is specified in milliseconds (1/1000 of a second). bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); // Notify a condition, releasing at one object waiting void Notify(); // Notify a condition, releasing all objects waiting void NotifyAll(); }; //----------------------------------------------------------------------------------- // ***** Event // Event is a wait-able synchronization object similar to Windows event. // Event can be waited on until it's signaled by another thread calling // either SetEvent or PulseEvent. class Event { // Event state, its mutex and the wait condition volatile bool State; volatile bool Temporary; mutable Mutex StateMutex; WaitCondition StateWaitCondition; void updateState(bool newState, bool newTemp, bool mustNotify); public: Event(bool setInitially = 0) : State(setInitially), Temporary(false) { } ~Event() { } // Wait on an event condition until it is set // Delay is specified in milliseconds (1/1000 of a second). bool Wait(unsigned delay = OVR_WAIT_INFINITE); // Set an event, releasing objects waiting on it void SetEvent() { updateState(true, false, true); } // Reset an event, un-signaling it void ResetEvent() { updateState(false, false, false); } // Set and then reset an event once a waiter is released. // If threads are already waiting, they will be notified and released // If threads are not waiting, the event is set until the first thread comes in void PulseEvent() { updateState(true, true, true); } }; //----------------------------------------------------------------------------------- // ***** Thread class // ThreadId uniquely identifies a thread; returned by GetCurrentThreadId() and // Thread::GetThreadId. typedef void* ThreadId; // *** Thread flags // Indicates that the thread is has been started, i.e. Start method has been called, and threads // OnExit() method has not yet been called/returned. #define OVR_THREAD_STARTED 0x01 // This flag is set once the thread has ran, and finished. #define OVR_THREAD_FINISHED 0x02 // This flag is set temporarily if this thread was started suspended. It is used internally. #define OVR_THREAD_START_SUSPENDED 0x08 // This flag is used to ask a thread to exit. Message driven threads will usually check this flag // and finish once it is set. #define OVR_THREAD_EXIT 0x10 class Thread : public RefCountBase { // NOTE: Waitable must be the first base since it implements RefCountImpl. public: // *** Callback functions, can be used instead of overriding Run // Run function prototypes. // Thread function and user handle passed to it, executed by the default // Thread::Run implementation if not null. typedef int (*ThreadFn)(Thread *pthread, void* h); // Thread ThreadFunction1 is executed if not 0, otherwise ThreadFunction2 is tried ThreadFn ThreadFunction; // User handle passes to a thread void* UserHandle; // Thread state to start a thread with enum ThreadState { NotRunning = 0, Running = 1, Suspended = 2 }; // Thread priority enum ThreadPriority { CriticalPriority, HighestPriority, AboveNormalPriority, NormalPriority, BelowNormalPriority, LowestPriority, IdlePriority, }; // Thread constructor parameters struct CreateParams { CreateParams(ThreadFn func = 0, void* hand = 0, UPInt ssize = 128 * 1024, int proc = -1, ThreadState state = NotRunning, ThreadPriority prior = NormalPriority) : threadFunction(func), userHandle(hand), stackSize(ssize), processor(proc), initialState(state), priority(prior) {} ThreadFn threadFunction; // Thread function void* userHandle; // User handle passes to a thread UPInt stackSize; // Thread stack size int processor; // Thread hardware processor ThreadState initialState; // ThreadPriority priority; // Thread priority }; // *** Constructors // A default constructor always creates a thread in NotRunning state, because // the derived class has not yet been initialized. The derived class can call Start explicitly. // "processor" parameter specifies which hardware processor this thread will be run on. // -1 means OS decides this. Implemented only on Win32 Thread(UPInt stackSize = 128 * 1024, int processor = -1); // Constructors that initialize the thread with a pointer to function. // An option to start a thread is available, but it should not be used if classes are derived from Thread. // "processor" parameter specifies which hardware processor this thread will be run on. // -1 means OS decides this. Implemented only on Win32 Thread(ThreadFn threadFunction, void* userHandle = 0, UPInt stackSize = 128 * 1024, int processor = -1, ThreadState initialState = NotRunning); // Constructors that initialize the thread with a create parameters structure. explicit Thread(const CreateParams& params); // Destructor. virtual ~Thread(); // Waits for all Threads to finish; should be called only from the root // application thread. Once this function returns, we know that all other // thread's references to Thread object have been released. static void OVR_CDECL FinishAllThreads(); // *** Overridable Run function for thread processing // - returning from this method will end the execution of the thread // - return value is usually 0 for success virtual int Run(); // Called after return/exit function virtual void OnExit(); // *** Thread management // Starts the thread if its not already running // - internally sets up the threading and calls Run() // - initial state can either be Running or Suspended, NotRunning will just fail and do nothing // - returns the exit code virtual bool Start(ThreadState initialState = Running); // Quits with an exit code virtual void Exit(int exitCode=0); // Suspend the thread until resumed // Returns 1 for success, 0 for failure. bool Suspend(); // Resumes currently suspended thread // Returns 1 for success, 0 for failure. bool Resume(); // Static function to return a pointer to the current thread //static Thread* GetThread(); // *** Thread status query functions bool GetExitFlag() const; void SetExitFlag(bool exitFlag); // Determines whether the thread was running and is now finished bool IsFinished() const; // Determines if the thread is currently suspended bool IsSuspended() const; // Returns current thread state ThreadState GetThreadState() const; // Returns the number of available CPUs on the system static int GetCPUCount(); // Returns the thread exit code. Exit code is initialized to 0, // and set to the return value if Run function after the thread is finished. inline int GetExitCode() const { return ExitCode; } // Returns an OS handle #if defined(OVR_OS_WIN32) void* GetOSHandle() const { return ThreadHandle; } #else pthread_t GetOSHandle() const { return ThreadHandle; } #endif #if defined(OVR_OS_WIN32) ThreadId GetThreadId() const { return IdValue; } #else ThreadId GetThreadId() const { return (ThreadId)GetOSHandle(); } #endif static int GetOSPriority(ThreadPriority); // *** Sleep // Sleep secs seconds static bool Sleep(unsigned secs); // Sleep msecs milliseconds static bool MSleep(unsigned msecs); // *** Debugging functionality #if defined(OVR_OS_WIN32) virtual void SetThreadName( const char* name ); #else virtual void SetThreadName( const char* name ) { OVR_UNUSED(name); } #endif private: #if defined(OVR_OS_WIN32) friend unsigned WINAPI Thread_Win32StartFn(void *pthread); #else friend void *Thread_PthreadStartFn(void * phandle); static int InitAttr; static pthread_attr_t Attr; #endif protected: // Thread state flags AtomicInt ThreadFlags; AtomicInt SuspendCount; UPInt StackSize; // Hardware processor which this thread is running on. int Processor; ThreadPriority Priority; #if defined(OVR_OS_WIN32) void* ThreadHandle; volatile ThreadId IdValue; // System-specific cleanup function called from destructor void CleanupSystemThread(); #else pthread_t ThreadHandle; #endif // Exit code of the thread, as returned by Run. int ExitCode; // Internal run function. int PRun(); // Finishes the thread and releases internal reference to it. void FinishAndRelease(); void Init(const CreateParams& params); // Protected copy constructor Thread(const Thread &source) : RefCountBase() { OVR_UNUSED(source); } }; // Returns the unique Id of a thread it is called on, intended for // comparison purposes. ThreadId GetCurrentThreadId(); } // OVR #endif // OVR_ENABLE_THREADS #endif // OVR_Threads_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp new file mode 100644 index 0000000..2069750 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_ThreadsPthread.cpp Content : Created : Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Threads.h" #include "OVR_Hash.h" #ifdef OVR_ENABLE_THREADS #include "OVR_Timer.h" #include "OVR_Log.h" #include #include #include #include #include namespace OVR { // ***** Mutex implementation // *** Internal Mutex implementation structure class MutexImpl : public NewOverrideBase { // System mutex or semaphore pthread_mutex_t SMutex; bool Recursive; unsigned LockCount; pthread_t LockedBy; friend class WaitConditionImpl; public: // Constructor/destructor MutexImpl(Mutex* pmutex, bool recursive = 1); ~MutexImpl(); // Locking functions void DoLock(); bool TryLock(); void Unlock(Mutex* pmutex); // Returns 1 if the mutes is currently locked bool IsLockedByAnotherThread(Mutex* pmutex); bool IsSignaled() const; }; pthread_mutexattr_t Lock::RecursiveAttr; bool Lock::RecursiveAttrInit = 0; // *** Constructor/destructor MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) { OVR_UNUSED(pmutex); Recursive = recursive; LockCount = 0; if (Recursive) { if (!Lock::RecursiveAttrInit) { pthread_mutexattr_init(&Lock::RecursiveAttr); pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); Lock::RecursiveAttrInit = 1; } pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); } else pthread_mutex_init(&SMutex, 0); } MutexImpl::~MutexImpl() { pthread_mutex_destroy(&SMutex); } // Lock and try lock void MutexImpl::DoLock() { while (pthread_mutex_lock(&SMutex)) ; LockCount++; LockedBy = pthread_self(); } bool MutexImpl::TryLock() { if (!pthread_mutex_trylock(&SMutex)) { LockCount++; LockedBy = pthread_self(); return 1; } return 0; } void MutexImpl::Unlock(Mutex* pmutex) { OVR_UNUSED(pmutex); OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); unsigned lockCount; LockCount--; lockCount = LockCount; pthread_mutex_unlock(&SMutex); } bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) { OVR_UNUSED(pmutex); // There could be multiple interpretations of IsLocked with respect to current thread if (LockCount == 0) return 0; if (pthread_self() != LockedBy) return 1; return 0; } bool MutexImpl::IsSignaled() const { // An mutex is signaled if it is not locked ANYWHERE // Note that this is different from IsLockedByAnotherThread function, // that takes current thread into account return LockCount == 0; } // *** Actual Mutex class implementation Mutex::Mutex(bool recursive) { // NOTE: RefCount mode already thread-safe for all waitables. pImpl = new MutexImpl(this, recursive); } Mutex::~Mutex() { delete pImpl; } // Lock and try lock void Mutex::DoLock() { pImpl->DoLock(); } bool Mutex::TryLock() { return pImpl->TryLock(); } void Mutex::Unlock() { pImpl->Unlock(this); } bool Mutex::IsLockedByAnotherThread() { return pImpl->IsLockedByAnotherThread(this); } //----------------------------------------------------------------------------------- // ***** Event bool Event::Wait(unsigned delay) { Mutex::Locker lock(&StateMutex); // Do the correct amount of waiting if (delay == OVR_WAIT_INFINITE) { while(!State) StateWaitCondition.Wait(&StateMutex); } else if (delay) { if (!State) StateWaitCondition.Wait(&StateMutex, delay); } bool state = State; // Take care of temporary 'pulsing' of a state if (Temporary) { Temporary = false; State = false; } return state; } void Event::updateState(bool newState, bool newTemp, bool mustNotify) { Mutex::Locker lock(&StateMutex); State = newState; Temporary = newTemp; if (mustNotify) StateWaitCondition.NotifyAll(); } // ***** Wait Condition Implementation // Internal implementation class class WaitConditionImpl : public NewOverrideBase { pthread_mutex_t SMutex; pthread_cond_t Condv; public: // Constructor/destructor WaitConditionImpl(); ~WaitConditionImpl(); // Release mutex and wait for condition. The mutex is re-aqured after the wait. bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); // Notify a condition, releasing at one object waiting void Notify(); // Notify a condition, releasing all objects waiting void NotifyAll(); }; WaitConditionImpl::WaitConditionImpl() { pthread_mutex_init(&SMutex, 0); pthread_cond_init(&Condv, 0); } WaitConditionImpl::~WaitConditionImpl() { pthread_mutex_destroy(&SMutex); pthread_cond_destroy(&Condv); } bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) { bool result = 1; unsigned lockCount = pmutex->pImpl->LockCount; // Mutex must have been locked if (lockCount == 0) return 0; pthread_mutex_lock(&SMutex); // Finally, release a mutex or semaphore if (pmutex->pImpl->Recursive) { // Release the recursive mutex N times pmutex->pImpl->LockCount = 0; for(unsigned i=0; ipImpl->SMutex); } else { pmutex->pImpl->LockCount = 0; pthread_mutex_unlock(&pmutex->pImpl->SMutex); } // Note that there is a gap here between mutex.Unlock() and Wait(). // The other mutex protects this gap. if (delay == OVR_WAIT_INFINITE) pthread_cond_wait(&Condv,&SMutex); else { timespec ts; struct timeval tv; gettimeofday(&tv, 0); ts.tv_sec = tv.tv_sec + (delay / 1000); ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; if (ts.tv_nsec > 999999999) { ts.tv_sec++; ts.tv_nsec -= 1000000000; } int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); OVR_ASSERT(r == 0 || r == ETIMEDOUT); if (r) result = 0; } pthread_mutex_unlock(&SMutex); // Re-aquire the mutex for(unsigned i=0; iDoLock(); // Return the result return result; } // Notify a condition, releasing the least object in a queue void WaitConditionImpl::Notify() { pthread_mutex_lock(&SMutex); pthread_cond_signal(&Condv); pthread_mutex_unlock(&SMutex); } // Notify a condition, releasing all objects waiting void WaitConditionImpl::NotifyAll() { pthread_mutex_lock(&SMutex); pthread_cond_broadcast(&Condv); pthread_mutex_unlock(&SMutex); } // *** Actual implementation of WaitCondition WaitCondition::WaitCondition() { pImpl = new WaitConditionImpl; } WaitCondition::~WaitCondition() { delete pImpl; } bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) { return pImpl->Wait(pmutex, delay); } // Notification void WaitCondition::Notify() { pImpl->Notify(); } void WaitCondition::NotifyAll() { pImpl->NotifyAll(); } // ***** Current thread // Per-thread variable /* static __thread Thread* pCurrentThread = 0; // Static function to return a pointer to the current thread void Thread::InitCurrentThread(Thread *pthread) { pCurrentThread = pthread; } // Static function to return a pointer to the current thread Thread* Thread::GetThread() { return pCurrentThread; } */ // *** Thread constructors. Thread::Thread(UPInt stackSize, int processor) { // NOTE: RefCount mode already thread-safe for all Waitable objects. CreateParams params; params.stackSize = stackSize; params.processor = processor; Init(params); } Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, int processor, Thread::ThreadState initialState) { CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); Init(params); } Thread::Thread(const CreateParams& params) { Init(params); } void Thread::Init(const CreateParams& params) { // Clear the variables ThreadFlags = 0; ThreadHandle = 0; ExitCode = 0; SuspendCount = 0; StackSize = params.stackSize; Processor = params.processor; Priority = params.priority; // Clear Function pointers ThreadFunction = params.threadFunction; UserHandle = params.userHandle; if (params.initialState != NotRunning) Start(params.initialState); } Thread::~Thread() { // Thread should not running while object is being destroyed, // this would indicate ref-counting issue. //OVR_ASSERT(IsRunning() == 0); // Clean up thread. ThreadHandle = 0; } // *** Overridable User functions. // Default Run implementation int Thread::Run() { // Call pointer to function, if available. return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; } void Thread::OnExit() { } // Finishes the thread and releases internal reference to it. void Thread::FinishAndRelease() { // Note: thread must be US. ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); ThreadFlags |= OVR_THREAD_FINISHED; // Release our reference; this is equivalent to 'delete this' // from the point of view of our thread. Release(); } // *** ThreadList - used to track all created threads class ThreadList : public NewOverrideBase { //------------------------------------------------------------------------ struct ThreadHashOp { size_t operator()(const Thread* ptr) { return (((size_t)ptr) >> 6) ^ (size_t)ptr; } }; HashSet ThreadSet; Mutex ThreadMutex; WaitCondition ThreadsEmpty; // Track the root thread that created us. pthread_t RootThreadId; static ThreadList* volatile pRunningThreads; void addThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Add(pthread); } void removeThread(Thread *pthread) { Mutex::Locker lock(&ThreadMutex); ThreadSet.Remove(pthread); if (ThreadSet.GetSize() == 0) ThreadsEmpty.Notify(); } void finishAllThreads() { // Only original root thread can call this. OVR_ASSERT(pthread_self() == RootThreadId); Mutex::Locker lock(&ThreadMutex); while (ThreadSet.GetSize() != 0) ThreadsEmpty.Wait(&ThreadMutex); } public: ThreadList() { RootThreadId = pthread_self(); } ~ThreadList() { } static void AddRunningThread(Thread *pthread) { // Non-atomic creation ok since only the root thread if (!pRunningThreads) { pRunningThreads = new ThreadList; OVR_ASSERT(pRunningThreads); } pRunningThreads->addThread(pthread); } // NOTE: 'pthread' might be a dead pointer when this is // called so it should not be accessed; it is only used // for removal. static void RemoveRunningThread(Thread *pthread) { OVR_ASSERT(pRunningThreads); pRunningThreads->removeThread(pthread); } static void FinishAllThreads() { // This is ok because only root thread can wait for other thread finish. if (pRunningThreads) { pRunningThreads->finishAllThreads(); delete pRunningThreads; pRunningThreads = 0; } } }; // By default, we have no thread list. ThreadList* volatile ThreadList::pRunningThreads = 0; // FinishAllThreads - exposed publicly in Thread. void Thread::FinishAllThreads() { ThreadList::FinishAllThreads(); } // *** Run override int Thread::PRun() { // Suspend us on start, if requested if (ThreadFlags & OVR_THREAD_START_SUSPENDED) { Suspend(); ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; } // Call the virtual run function ExitCode = Run(); return ExitCode; } // *** User overridables bool Thread::GetExitFlag() const { return (ThreadFlags & OVR_THREAD_EXIT) != 0; } void Thread::SetExitFlag(bool exitFlag) { // The below is atomic since ThreadFlags is AtomicInt. if (exitFlag) ThreadFlags |= OVR_THREAD_EXIT; else ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; } // Determines whether the thread was running and is now finished bool Thread::IsFinished() const { return (ThreadFlags & OVR_THREAD_FINISHED) != 0; } // Determines whether the thread is suspended bool Thread::IsSuspended() const { return SuspendCount > 0; } // Returns current thread state Thread::ThreadState Thread::GetThreadState() const { if (IsSuspended()) return Suspended; if (ThreadFlags & OVR_THREAD_STARTED) return Running; return NotRunning; } /* static const char* mapsched_policy(int policy) { switch(policy) { case SCHED_OTHER: return "SCHED_OTHER"; case SCHED_RR: return "SCHED_RR"; case SCHED_FIFO: return "SCHED_FIFO"; } return "UNKNOWN"; } int policy; sched_param sparam; pthread_getschedparam(pthread_self(), &policy, &sparam); int max_prior = sched_get_priority_max(policy); int min_prior = sched_get_priority_min(policy); printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); #include */ // ***** Thread management // The actual first function called on thread start void* Thread_PthreadStartFn(void* phandle) { Thread* pthread = (Thread*)phandle; int result = pthread->PRun(); // Signal the thread as done and release it atomically. pthread->FinishAndRelease(); // At this point Thread object might be dead; however we can still pass // it to RemoveRunningThread since it is only used as a key there. ThreadList::RemoveRunningThread(pthread); return reinterpret_cast(result); } int Thread::InitAttr = 0; pthread_attr_t Thread::Attr; /* static */ int Thread::GetOSPriority(ThreadPriority p) //static inline int MapToSystemPrority(Thread::ThreadPriority p) { OVR_UNUSED(p); return -1; } bool Thread::Start(ThreadState initialState) { if (initialState == NotRunning) return 0; if (GetThreadState() != NotRunning) { OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); return 0; } if (!InitAttr) { pthread_attr_init(&Attr); pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&Attr, 128 * 1024); sched_param sparam; sparam.sched_priority = Thread::GetOSPriority(NormalPriority); pthread_attr_setschedparam(&Attr, &sparam); InitAttr = 1; } ExitCode = 0; SuspendCount = 0; ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; // AddRef to us until the thread is finished AddRef(); ThreadList::AddRunningThread(this); int result; if (StackSize != 128 * 1024 || Priority != NormalPriority) { pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&attr, StackSize); sched_param sparam; sparam.sched_priority = Thread::GetOSPriority(Priority); pthread_attr_setschedparam(&attr, &sparam); result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); pthread_attr_destroy(&attr); } else result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); if (result) { ThreadFlags = 0; Release(); ThreadList::RemoveRunningThread(this); return 0; } return 1; } // Suspend the thread until resumed bool Thread::Suspend() { OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); return 0; } // Resumes currently suspended thread bool Thread::Resume() { return 0; } // Quits with an exit code void Thread::Exit(int exitCode) { // Can only exist the current thread // if (GetThread() != this) // return; // Call the virtual OnExit function OnExit(); // Signal this thread object as done and release it's references. FinishAndRelease(); ThreadList::RemoveRunningThread(this); pthread_exit(reinterpret_cast(exitCode)); } ThreadId GetCurrentThreadId() { return (void*)pthread_self(); } // *** Sleep functions /* static */ bool Thread::Sleep(unsigned secs) { sleep(secs); return 1; } /* static */ bool Thread::MSleep(unsigned msecs) { usleep(msecs*1000); return 1; } /* static */ int Thread::GetCPUCount() { return 1; } } #endif // OVR_ENABLE_THREADS \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Timer.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Timer.cpp new file mode 100644 index 0000000..d03762f --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Timer.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Timer.cpp Content : Provides static functions for precise timing Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Timer.h" #include "OVR_Log.h" #if defined (OVR_OS_WIN32) #include #elif defined(OVR_OS_ANDROID) #include #include #else #include #endif namespace OVR { // For recorded data playback bool Timer::useFakeSeconds = false; double Timer::FakeSeconds = 0; //------------------------------------------------------------------------ // *** Timer - Platform Independent functions // Returns global high-resolution application timer in seconds. double Timer::GetSeconds() { if(useFakeSeconds) return FakeSeconds; return double(Timer::GetTicksNanos()) * 0.000000001; } #ifndef OVR_OS_WIN32 // Unused on OSs other then Win32. void Timer::initializeTimerSystem() { } void Timer::shutdownTimerSystem() { } #endif //------------------------------------------------------------------------ // *** Android Specific Timer #if defined(OVR_OS_ANDROID) UInt64 Timer::GetTicksNanos() { if (useFakeSeconds) return (UInt64) (FakeSeconds * NanosPerSecond); // Choreographer vsync timestamp is based on. struct timespec tp; const int status = clock_gettime(CLOCK_MONOTONIC, &tp); if (status != 0) { OVR_DEBUG_LOG(("clock_gettime status=%i", status )); } const UInt64 result = (UInt64)tp.tv_sec * (UInt64)(1000 * 1000 * 1000) + UInt64(tp.tv_nsec); return result; } //------------------------------------------------------------------------ // *** Win32 Specific Timer #elif defined (OVR_OS_WIN32) // This helper class implements high-resolution wrapper that combines timeGetTime() output // with QueryPerformanceCounter. timeGetTime() is lower precision but drives the high bits, // as it's tied to the system clock. struct PerformanceTimer { PerformanceTimer() : OldMMTimeMs(0), MMTimeWrapCounter(0), PrefFrequency(0), LastResultNanos(0), PerfMinusTicksDeltaNanos(0) { } enum { MMTimerResolutionNanos = 1000000 }; void Initialize(); void Shutdown(); UInt64 GetTimeNanos(); UINT64 getFrequency() { if (PrefFrequency == 0) { LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); PrefFrequency = freq.QuadPart; } return PrefFrequency; } CRITICAL_SECTION TimeCS; // timeGetTime() support with wrap. UInt32 OldMMTimeMs; UInt32 MMTimeWrapCounter; // Cached performance frequency result. UInt64 PrefFrequency; // Computed as (perfCounterNanos - ticksCounterNanos) initially, // and used to adjust timing. UInt64 PerfMinusTicksDeltaNanos; // Last returned value in nanoseconds, to ensure we don't back-step in time. UInt64 LastResultNanos; }; PerformanceTimer Win32_PerfTimer; void PerformanceTimer::Initialize() { timeBeginPeriod(1); InitializeCriticalSection(&TimeCS); MMTimeWrapCounter = 0; getFrequency(); } void PerformanceTimer::Shutdown() { DeleteCriticalSection(&TimeCS); timeEndPeriod(1); } UInt64 PerformanceTimer::GetTimeNanos() { UInt64 resultNanos; LARGE_INTEGER li; DWORD mmTimeMs; // On Win32 QueryPerformanceFrequency is unreliable due to SMP and // performance levels, so use this logic to detect wrapping and track // high bits. ::EnterCriticalSection(&TimeCS); // Get raw value and perf counter "At the same time". mmTimeMs = timeGetTime(); QueryPerformanceCounter(&li); if (OldMMTimeMs > mmTimeMs) MMTimeWrapCounter++; OldMMTimeMs = mmTimeMs; // Normalize to nanoseconds. UInt64 mmCounterNanos = ((UInt64(MMTimeWrapCounter) << 32) | mmTimeMs) * 1000000; UInt64 frequency = getFrequency(); UInt64 perfCounterSeconds = UInt64(li.QuadPart) / frequency; UInt64 perfRemainderNanos = ( (UInt64(li.QuadPart) - perfCounterSeconds * frequency) * Timer::NanosPerSecond ) / frequency; UInt64 perfCounterNanos = perfCounterSeconds * Timer::NanosPerSecond + perfRemainderNanos; if (PerfMinusTicksDeltaNanos == 0) PerfMinusTicksDeltaNanos = perfCounterNanos - mmCounterNanos; // Compute result before snapping. // // On first call, this evaluates to: // resultNanos = mmCounterNanos. // Next call, assuming no wrap: // resultNanos = prev_mmCounterNanos + (perfCounterNanos - prev_perfCounterNanos). // After wrap, this would be: // resultNanos = snapped(prev_mmCounterNanos +/- 1ms) + (perfCounterNanos - prev_perfCounterNanos). // resultNanos = perfCounterNanos - PerfMinusTicksDeltaNanos; // Snap the range so that resultNanos never moves further apart then its target resolution. // It's better to allow more slack on the high side as timeGetTime() may be updated at sporadically // larger then 1 ms intervals even when 1 ms resolution is requested. if (resultNanos > (mmCounterNanos + MMTimerResolutionNanos*2)) { resultNanos = mmCounterNanos + MMTimerResolutionNanos*2; if (resultNanos < LastResultNanos) resultNanos = LastResultNanos; PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos; } else if (resultNanos < (mmCounterNanos - MMTimerResolutionNanos)) { resultNanos = mmCounterNanos - MMTimerResolutionNanos; if (resultNanos < LastResultNanos) resultNanos = LastResultNanos; PerfMinusTicksDeltaNanos = perfCounterNanos - resultNanos; } LastResultNanos = resultNanos; ::LeaveCriticalSection(&TimeCS); //Tom's addition, to keep precision static UInt64 initial_time = 0; if (!initial_time) initial_time = resultNanos; resultNanos -= initial_time; return resultNanos; } // Delegate to PerformanceTimer. UInt64 Timer::GetTicksNanos() { if (useFakeSeconds) return (UInt64) (FakeSeconds * NanosPerSecond); return Win32_PerfTimer.GetTimeNanos(); } void Timer::initializeTimerSystem() { Win32_PerfTimer.Initialize(); } void Timer::shutdownTimerSystem() { Win32_PerfTimer.Shutdown(); } #else // !OVR_OS_WIN32 && !OVR_OS_ANDROID //------------------------------------------------------------------------ // *** Standard OS Timer UInt64 Timer::GetTicksNanos() { if (useFakeSeconds) return (UInt64) (FakeSeconds * NanosPerSecond); // TODO: prefer rdtsc when available? UInt64 result; // Return microseconds. struct timeval tv; gettimeofday(&tv, 0); result = (UInt64)tv.tv_sec * 1000000; result += tv.tv_usec; return result * 1000; } #endif // OS-specific } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Timer.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Timer.h new file mode 100644 index 0000000..12166a1 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Timer.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR Filename : OVR_Timer.h Content : Provides static functions for precise timing Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Timer_h #define OVR_Timer_h #include "OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** Timer // Timer class defines a family of static functions used for application // timing and profiling. class Timer { public: enum { MsPerSecond = 1000, // Milliseconds in one second. NanosPerSecond = MsPerSecond * 1000 * 1000, MksPerSecond = MsPerSecond * 1000 }; // ***** Timing APIs for Application // These APIs should be used to guide animation and other program functions // that require precision. // Returns global high-resolution application timer in seconds. static double OVR_STDCALL GetSeconds(); // Returns time in Nanoseconds, using highest possible system resolution. static UInt64 OVR_STDCALL GetTicksNanos(); // Kept for compatibility. // Returns ticks in milliseconds, as a 32-bit number. May wrap around every 49.2 days. // Use either time difference of two values of GetTicks to avoid wrap-around. static UInt32 OVR_STDCALL GetTicksMs() { return UInt32(GetTicksNanos() / 1000000); } // for recorded data playback static void SetFakeSeconds(double fakeSeconds) { FakeSeconds = fakeSeconds; useFakeSeconds = true; } private: friend class System; // System called during program startup/shutdown. static void initializeTimerSystem(); static void shutdownTimerSystem(); // for recorded data playback static double FakeSeconds; static bool useFakeSeconds; }; } // OVR::Timer #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Types.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Types.h new file mode 100644 index 0000000..34a2959 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_Types.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Types.h Content : Standard library defines and simple types Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Types_H #define OVR_Types_H //----------------------------------------------------------------------------------- // ****** Operating System // // Type definitions exist for the following operating systems: (OVR_OS_x) // // WIN32 - Win32 (Windows 95/98/ME and Windows NT/2000/XP) // DARWIN - Darwin OS (Mac OS X) // LINUX - Linux // ANDROID - Android // IPHONE - iPhone #if (defined(__APPLE__) && (defined(__GNUC__) ||\ defined(__xlC__) || defined(__xlc__))) || defined(__MACOS__) # if (defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) || defined(__IPHONE_OS_VERSION_MIN_REQUIRED)) # define OVR_OS_IPHONE # else # define OVR_OS_DARWIN # define OVR_OS_MAC # endif #elif (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) # define OVR_OS_WIN32 #elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)) # define OVR_OS_WIN32 #elif defined(__linux__) || defined(__linux) # define OVR_OS_LINUX #else # define OVR_OS_OTHER #endif #if defined(ANDROID) # define OVR_OS_ANDROID #endif //----------------------------------------------------------------------------------- // ***** CPU Architecture // // The following CPUs are defined: (OVR_CPU_x) // // X86 - x86 (IA-32) // X86_64 - x86_64 (amd64) // PPC - PowerPC // PPC64 - PowerPC64 // MIPS - MIPS // OTHER - CPU for which no special support is present or needed #if defined(__x86_64__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) # define OVR_CPU_X86_64 # define OVR_64BIT_POINTERS #elif defined(__i386__) || defined(OVR_OS_WIN32) # define OVR_CPU_X86 #elif defined(__powerpc64__) # define OVR_CPU_PPC64 #elif defined(__ppc__) # define OVR_CPU_PPC #elif defined(__mips__) || defined(__MIPSEL__) # define OVR_CPU_MIPS #elif defined(__arm__) # define OVR_CPU_ARM #else # define OVR_CPU_OTHER #endif //----------------------------------------------------------------------------------- // ***** Co-Processor Architecture // // The following co-processors are defined: (OVR_CPU_x) // // SSE - Available on all modern x86 processors. // Altivec - Available on all modern ppc processors. // Neon - Available on some armv7+ processors. #if defined(__SSE__) || defined(OVR_OS_WIN32) # define OVR_CPU_SSE #endif // __SSE__ #if defined( __ALTIVEC__ ) # define OVR_CPU_ALTIVEC #endif // __ALTIVEC__ #if defined(__ARM_NEON__) # define OVR_CPU_ARM_NEON #endif // __ARM_NEON__ //----------------------------------------------------------------------------------- // ***** Compiler // // The following compilers are defined: (OVR_CC_x) // // MSVC - Microsoft Visual C/C++ // INTEL - Intel C++ for Linux / Windows // GNU - GNU C++ // ARM - ARM C/C++ #if defined(__INTEL_COMPILER) // Intel 4.0 = 400 // Intel 5.0 = 500 // Intel 6.0 = 600 // Intel 8.0 = 800 // Intel 9.0 = 900 # define OVR_CC_INTEL __INTEL_COMPILER #elif defined(_MSC_VER) // MSVC 5.0 = 1100 // MSVC 6.0 = 1200 // MSVC 7.0 (VC2002) = 1300 // MSVC 7.1 (VC2003) = 1310 // MSVC 8.0 (VC2005) = 1400 // MSVC 9.0 (VC2008) = 1500 // MSVC 10.0 (VC2010) = 1600 // MSVC 11.0 (VC2012) = 1700 // MSVC 12.0 (VC2013) = 1800 # define OVR_CC_MSVC _MSC_VER #elif defined(__GNUC__) # define OVR_CC_GNU #elif defined(__CC_ARM) # define OVR_CC_ARM #else # error "Oculus does not support this Compiler" #endif //----------------------------------------------------------------------------------- // ***** Compiler Warnings // Disable MSVC warnings #if defined(OVR_CC_MSVC) # pragma warning(disable : 4127) // Inconsistent dll linkage # pragma warning(disable : 4530) // Exception handling # if (OVR_CC_MSVC<1300) # pragma warning(disable : 4514) // Unreferenced inline function has been removed # pragma warning(disable : 4710) // Function not inlined # pragma warning(disable : 4714) // _force_inline not inlined # pragma warning(disable : 4786) // Debug variable name longer than 255 chars # endif // (OVR_CC_MSVC<1300) #endif // (OVR_CC_MSVC) // *** Linux Unicode - must come before Standard Includes #ifdef OVR_OS_LINUX // Use glibc unicode functions on linux. # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif #endif //----------------------------------------------------------------------------------- // ***** Standard Includes // #include #include #include // MSVC Based Memory Leak checking - for now #if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG) # define _CRTDBG_MAP_ALLOC # include # include #if 0 // Uncomment this to help debug memory leaks under Visual Studio in OVR apps only. // This shouldn't be defined in customer releases. # ifndef OVR_DEFINE_NEW # define OVR_DEFINE_NEW new(__FILE__, __LINE__) # define new OVR_DEFINE_NEW # endif #endif #endif //----------------------------------------------------------------------------------- // ***** Type definitions for Common Systems namespace OVR { typedef char Char; // Pointer-sized integer typedef size_t UPInt; typedef ptrdiff_t SPInt; #if defined(OVR_OS_WIN32) typedef char SByte; // 8 bit Integer (Byte) typedef unsigned char UByte; typedef short SInt16; // 16 bit Integer (Word) typedef unsigned short UInt16; typedef long SInt32; // 32 bit Integer typedef unsigned long UInt32; typedef __int64 SInt64; // 64 bit Integer (QWord) typedef unsigned __int64 UInt64; #elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) || defined(OVR_CC_GNU) typedef int SByte __attribute__((__mode__ (__QI__))); typedef unsigned int UByte __attribute__((__mode__ (__QI__))); typedef int SInt16 __attribute__((__mode__ (__HI__))); typedef unsigned int UInt16 __attribute__((__mode__ (__HI__))); typedef int SInt32 __attribute__((__mode__ (__SI__))); typedef unsigned int UInt32 __attribute__((__mode__ (__SI__))); typedef int SInt64 __attribute__((__mode__ (__DI__))); typedef unsigned int UInt64 __attribute__((__mode__ (__DI__))); #else #include typedef int8_t SByte; typedef uint8_t UByte; typedef int16_t SInt16; typedef uint16_t UInt16; typedef int32_t SInt32; typedef uint32_t UInt32; typedef int64_t SInt64; typedef uint64_t UInt64; #endif // ***** BaseTypes Namespace // BaseTypes namespace is explicitly declared to allow base types to be used // by customers directly without other contents of OVR namespace. // // Its is expected that OVR samples will declare 'using namespace OVR::BaseTypes' // to allow using these directly without polluting the target scope with other // OVR declarations, such as Ptr<>, String or Mutex. namespace BaseTypes { using OVR::UPInt; using OVR::SPInt; using OVR::UByte; using OVR::SByte; using OVR::UInt16; using OVR::SInt16; using OVR::UInt32; using OVR::SInt32; using OVR::UInt64; using OVR::SInt64; } // OVR::BaseTypes } // OVR //----------------------------------------------------------------------------------- // ***** Macro Definitions // // We define the following: // // OVR_BYTE_ORDER - Defined to either OVR_LITTLE_ENDIAN or OVR_BIG_ENDIAN // OVR_FORCE_INLINE - Forces inline expansion of function // OVR_ASM - Assembly language prefix // OVR_STR - Prefixes string with L"" if building unicode // // OVR_STDCALL - Use stdcall calling convention (Pascal arg order) // OVR_CDECL - Use cdecl calling convention (C argument order) // OVR_FASTCALL - Use fastcall calling convention (registers) // // Byte order constants, OVR_BYTE_ORDER is defined to be one of these. #define OVR_LITTLE_ENDIAN 1 #define OVR_BIG_ENDIAN 2 // Force inline substitute - goes before function declaration #if defined(OVR_CC_MSVC) # define OVR_FORCE_INLINE __forceinline #elif defined(OVR_CC_GNU) # define OVR_FORCE_INLINE __attribute__((always_inline)) inline #else # define OVR_FORCE_INLINE inline #endif // OVR_CC_MSVC #if defined(OVR_OS_WIN32) // ***** Win32 // Byte order #define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN // Calling convention - goes after function return type but before function name #ifdef __cplusplus_cli # define OVR_FASTCALL __stdcall #else # define OVR_FASTCALL __fastcall #endif #define OVR_STDCALL __stdcall #define OVR_CDECL __cdecl // Assembly macros #if defined(OVR_CC_MSVC) # define OVR_ASM _asm #else # define OVR_ASM asm #endif // (OVR_CC_MSVC) #ifdef UNICODE # define OVR_STR(str) L##str #else # define OVR_STR(str) str #endif // UNICODE #else // **** Standard systems #if (defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN))|| \ (defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) # define OVR_BYTE_ORDER OVR_BIG_ENDIAN #elif (defined(__ARMEB__) || defined(OVR_CPU_PPC) || defined(OVR_CPU_PPC64)) # define OVR_BYTE_ORDER OVR_BIG_ENDIAN #else # define OVR_BYTE_ORDER OVR_LITTLE_ENDIAN #endif // Assembly macros #define OVR_ASM __asm__ #define OVR_ASM_PROC(procname) OVR_ASM #define OVR_ASM_END OVR_ASM // Calling convention - goes after function return type but before function name #define OVR_FASTCALL #define OVR_STDCALL #define OVR_CDECL #endif // defined(OVR_OS_WIN32) //----------------------------------------------------------------------------------- // ***** OVR_DEBUG_BREAK, OVR_ASSERT // // If not in debug build, macros do nothing #ifndef OVR_BUILD_DEBUG # define OVR_DEBUG_CODE(c) c # define OVR_DEBUG_BREAK ((void)0) # define OVR_ASSERT(p) ((void)0) #else // Microsoft Win32 specific debugging support #if defined(OVR_OS_WIN32) # ifdef OVR_CPU_X86 # if defined(__cplusplus_cli) # define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) # elif defined(OVR_CC_GNU) # define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) # else # define OVR_DEBUG_BREAK do { OVR_ASM int 3 } while (0) # endif # else # define OVR_DEBUG_BREAK do { __debugbreak(); } while(0) # endif // Unix specific debugging support #elif defined(OVR_CPU_X86) || defined(OVR_CPU_X86_64) # define OVR_DEBUG_BREAK do { OVR_ASM("int $3\n\t"); } while(0) #else # define OVR_DEBUG_BREAK do { *((int *) 0) = 1; } while(0) #endif #define OVR_DEBUG_CODE(c) // This will cause compiler breakpoint #define OVR_ASSERT(p) do { if (!(p)) { OVR_DEBUG_BREAK; } } while(0) #endif // OVR_BUILD_DEBUG // Compile-time assert; produces compiler error if condition is false #define OVR_COMPILER_ASSERT(x) { int zero = 0; switch(zero) {case 0: case x:;} } //----------------------------------------------------------------------------------- // ***** OVR_UNUSED - Unused Argument handling // Macro to quiet compiler warnings about unused parameters/variables. #if defined(OVR_CC_GNU) # define OVR_UNUSED(a) do {__typeof__ (&a) __attribute__ ((unused)) __tmp = &a; } while(0) #else # define OVR_UNUSED(a) (a) #endif #define OVR_UNUSED1(a1) OVR_UNUSED(a1) #define OVR_UNUSED2(a1,a2) OVR_UNUSED(a1); OVR_UNUSED(a2) #define OVR_UNUSED3(a1,a2,a3) OVR_UNUSED2(a1,a2); OVR_UNUSED(a3) #define OVR_UNUSED4(a1,a2,a3,a4) OVR_UNUSED3(a1,a2,a3); OVR_UNUSED(a4) #define OVR_UNUSED5(a1,a2,a3,a4,a5) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED(a5) #define OVR_UNUSED6(a1,a2,a3,a4,a5,a6) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED2(a5,a6) #define OVR_UNUSED7(a1,a2,a3,a4,a5,a6,a7) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED3(a5,a6,a7) #define OVR_UNUSED8(a1,a2,a3,a4,a5,a6,a7,a8) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED4(a5,a6,a7,a8) #define OVR_UNUSED9(a1,a2,a3,a4,a5,a6,a7,a8,a9) OVR_UNUSED4(a1,a2,a3,a4); OVR_UNUSED5(a5,a6,a7,a8,a9) //----------------------------------------------------------------------------------- // ***** Configuration Macros // SF Build type #ifdef OVR_BUILD_DEBUG # define OVR_BUILD_STRING "Debug" #else # define OVR_BUILD_STRING "Release" #endif //// Enables SF Debugging information //# define OVR_BUILD_DEBUG // OVR_DEBUG_STATEMENT injects a statement only in debug builds. // OVR_DEBUG_SELECT injects first argument in debug builds, second argument otherwise. #ifdef OVR_BUILD_DEBUG #define OVR_DEBUG_STATEMENT(s) s #define OVR_DEBUG_SELECT(d, nd) d #else #define OVR_DEBUG_STATEMENT(s) #define OVR_DEBUG_SELECT(d, nd) nd #endif #define OVR_ENABLE_THREADS // // Prevents OVR from defining new within // type macros, so developers can override // new using the #define new new(...) trick // - used with OVR_DEFINE_NEW macro //# define OVR_BUILD_DEFINE_NEW // #endif // OVR_Types_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_UTF8Util.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_UTF8Util.cpp new file mode 100644 index 0000000..e49468d --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_UTF8Util.cpp @@ -0,0 +1 @@ +/************************************************************************** Filename : OVR_UTF8Util.cpp Content : UTF8 Unicode character encoding/decoding support Created : September 19, 2012 Notes : Notes : Much useful info at "UTF-8 and Unicode FAQ" http://www.cl.cam.ac.uk/~mgk25/unicode.html Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_UTF8Util.h" namespace OVR { namespace UTF8Util { SPInt OVR_STDCALL GetLength(const char* buf, SPInt buflen) { const char* p = buf; SPInt length = 0; if (buflen != -1) { while (p - buf < buflen) { // We should be able to have ASStrings with 0 in the middle. UTF8Util::DecodeNextChar_Advance0(&p); length++; } } else { while (UTF8Util::DecodeNextChar_Advance0(&p)) length++; } return length; } UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length) { const char* buf = putf8str; UInt32 c = 0; if (length != -1) { while (buf - putf8str < length) { c = UTF8Util::DecodeNextChar_Advance0(&buf); if (index == 0) return c; index--; } return c; } do { c = UTF8Util::DecodeNextChar_Advance0(&buf); index--; if (c == 0) { // We've hit the end of the string; don't go further. OVR_ASSERT(index == 0); return c; } } while (index >= 0); return c; } SPInt OVR_STDCALL GetByteIndex(SPInt index, const char *putf8str, SPInt length) { const char* buf = putf8str; if (length != -1) { while ((buf - putf8str) < length && index > 0) { UTF8Util::DecodeNextChar_Advance0(&buf); index--; } return buf-putf8str; } while (index > 0) { UInt32 c = UTF8Util::DecodeNextChar_Advance0(&buf); index--; if (c == 0) return buf-putf8str; }; return buf-putf8str; } int OVR_STDCALL GetEncodeCharSize(UInt32 ucs_character) { if (ucs_character <= 0x7F) return 1; else if (ucs_character <= 0x7FF) return 2; else if (ucs_character <= 0xFFFF) return 3; else if (ucs_character <= 0x1FFFFF) return 4; else if (ucs_character <= 0x3FFFFFF) return 5; else if (ucs_character <= 0x7FFFFFFF) return 6; else return 0; } UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer) { UInt32 uc; char c; // Security considerations: // // Changed, this is now only the case for DecodeNextChar: // - If we hit a zero byte, we want to return 0 without stepping // the buffer pointer past the 0. th // // If we hit an "overlong sequence"; i.e. a character encoded // in a longer multibyte string than is necessary, then we // need to discard the character. This is so attackers can't // disguise dangerous characters or character sequences -- // there is only one valid encoding for each character. // // If we decode characters { 0xD800 .. 0xDFFF } or { 0xFFFE, // 0xFFFF } then we ignore them; they are not valid in UTF-8. // This isn't actually an invalid character; it's a valid char that // looks like an inverted question mark. #define INVALID_CHAR 0x0FFFD #define FIRST_BYTE(mask, shift) \ uc = (c & (mask)) << (shift); #define NEXT_BYTE(shift) \ c = **putf8Buffer; \ if (c == 0) return 0; /* end of buffer, do not advance */ \ if ((c & 0xC0) != 0x80) return INVALID_CHAR; /* standard check */ \ (*putf8Buffer)++; \ uc |= (c & 0x3F) << shift; c = **putf8Buffer; (*putf8Buffer)++; if (c == 0) return 0; // End of buffer. if ((c & 0x80) == 0) return (UInt32) c; // Conventional 7-bit ASCII. // Multi-byte sequences. if ((c & 0xE0) == 0xC0) { // Two-byte sequence. FIRST_BYTE(0x1F, 6); NEXT_BYTE(0); if (uc < 0x80) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xF0) == 0xE0) { // Three-byte sequence. FIRST_BYTE(0x0F, 12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x800) return INVALID_CHAR; // overlong // Not valid ISO 10646, but Flash requires these to work // see AS3 test e15_5_3_2_3 for String.fromCharCode().charCodeAt(0) // if (uc >= 0x0D800 && uc <= 0x0DFFF) return INVALID_CHAR; // if (uc == 0x0FFFE || uc == 0x0FFFF) return INVALID_CHAR; // not valid ISO 10646 return uc; } else if ((c & 0xF8) == 0xF0) { // Four-byte sequence. FIRST_BYTE(0x07, 18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x010000) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xFC) == 0xF8) { // Five-byte sequence. FIRST_BYTE(0x03, 24); NEXT_BYTE(18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x0200000) return INVALID_CHAR; // overlong return uc; } else if ((c & 0xFE) == 0xFC) { // Six-byte sequence. FIRST_BYTE(0x01, 30); NEXT_BYTE(24); NEXT_BYTE(18); NEXT_BYTE(12); NEXT_BYTE(6); NEXT_BYTE(0); if (uc < 0x04000000) return INVALID_CHAR; // overlong return uc; } else { // Invalid. return INVALID_CHAR; } } void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* pindex, UInt32 ucs_character) { if (ucs_character <= 0x7F) { // Plain single-byte ASCII. pbuffer[(*pindex)++] = (char) ucs_character; } else if (ucs_character <= 0x7FF) { // Two bytes. pbuffer[(*pindex)++] = 0xC0 | (char)(ucs_character >> 6); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0xFFFF) { // Three bytes. pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x1FFFFF) { // Four bytes. pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x3FFFFFF) { // Five bytes. pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else if (ucs_character <= 0x7FFFFFFF) { // Six bytes. pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 24) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 18) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 12) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 6) & 0x3F); pbuffer[(*pindex)++] = 0x80 | (char)((ucs_character >> 0) & 0x3F); } else { // Invalid char; don't encode anything. } } SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length) { SPInt len = 0; if (length != -1) for (int i = 0; i < length; i++) { len += GetEncodeCharSize(pchar[i]); } else for (int i = 0;; i++) { if (pchar[i] == 0) return len; len += GetEncodeCharSize(pchar[i]); } return len; } void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length) { SPInt ofs = 0; if (length != -1) { for (int i = 0; i < length; i++) { EncodeChar(pbuff, &ofs, pchar[i]); } } else { for (int i = 0;; i++) { if (pchar[i] == 0) break; EncodeChar(pbuff, &ofs, pchar[i]); } } pbuff[ofs] = 0; } UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen) { wchar_t *pbegin = pbuff; if (bytesLen == -1) { while (1) { UInt32 ch = DecodeNextChar_Advance0(&putf8str); if (ch == 0) break; else if (ch >= 0xFFFF) ch = 0xFFFD; *pbuff++ = wchar_t(ch); } } else { const char* p = putf8str; while ((p - putf8str) < bytesLen) { UInt32 ch = DecodeNextChar_Advance0(&p); if (ch >= 0xFFFF) ch = 0xFFFD; *pbuff++ = wchar_t(ch); } } *pbuff = 0; return pbuff - pbegin; } #ifdef UTF8_UNIT_TEST // Compile this test case with something like: // // gcc utf8.cpp -g -I.. -DUTF8_UNIT_TEST -lstdc++ -o utf8_test // // or // // cl utf8.cpp -Zi -Od -DUTF8_UNIT_TEST -I.. // // If possible, try running the test program with the first arg // pointing at the file: // // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt // // and examine the results by eye to make sure they are acceptable to // you. #include "base/utility.h" #include bool check_equal(const char* utf8_in, const UInt32* ucs_in) { for (;;) { UInt32 next_ucs = *ucs_in++; UInt32 next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in); if (next_ucs != next_ucs_from_utf8) { return false; } if (next_ucs == 0) { OVR_ASSERT(next_ucs_from_utf8 == 0); break; } } return true; } void log_ascii(const char* line) { for (;;) { unsigned char c = (unsigned char) *line++; if (c == 0) { // End of line. return; } else if (c != '\n' && (c < 32 || c > 127)) { // Non-printable as plain ASCII. printf("<0x%02X>", (int) c); } else { printf("%c", c); } } } void log_ucs(const UInt32* line) { for (;;) { UInt32 uc = *line++; if (uc == 0) { // End of line. return; } else if (uc != '\n' && (uc < 32 || uc > 127)) { // Non-printable as plain ASCII. printf("", uc); } else { printf("%c", (char) uc); } } } // Simple canned test. int main(int argc, const char* argv[]) { { const char* test8 = "Ignacio Castaño"; const UInt32 test32[] = { 0x49, 0x67, 0x6E, 0x61, 0x63, 0x69, 0x6F, 0x20, 0x43, 0x61, 0x73, 0x74, 0x61, 0xF1, 0x6F, 0x00 }; OVR_ASSERT(check_equal(test8, test32)); } // If user passed an arg, try reading the file as UTF-8 encoded text. if (argc > 1) { const char* filename = argv[1]; FILE* fp = fopen(filename, "rb"); if (fp == NULL) { printf("Can't open file '%s'\n", filename); return 1; } // Read lines from the file, encode/decode them, and highlight discrepancies. const int LINE_SIZE = 200; // max line size char line_buffer_utf8[LINE_SIZE]; char reencoded_utf8[6 * LINE_SIZE]; UInt32 line_buffer_ucs[LINE_SIZE]; int byte_counter = 0; for (;;) { int c = fgetc(fp); if (c == EOF) { // Done. break; } line_buffer_utf8[byte_counter++] = c; if (c == '\n' || byte_counter >= LINE_SIZE - 2) { // End of line. Process the line. line_buffer_utf8[byte_counter++] = 0; // terminate. // Decode into UCS. const char* p = line_buffer_utf8; UInt32* q = line_buffer_ucs; for (;;) { UInt32 uc = UTF8Util::DecodeNextChar(&p); *q++ = uc; OVR_ASSERT(q < line_buffer_ucs + LINE_SIZE); OVR_ASSERT(p < line_buffer_utf8 + LINE_SIZE); if (uc == 0) break; } // Encode back into UTF-8. q = line_buffer_ucs; int index = 0; for (;;) { UInt32 uc = *q++; OVR_ASSERT(index < LINE_SIZE * 6 - 6); int last_index = index; UTF8Util::EncodeChar(reencoded_utf8, &index, uc); OVR_ASSERT(index <= last_index + 6); if (uc == 0) break; } // This can be useful for debugging. #if 0 // Show the UCS and the re-encoded UTF-8. log_ucs(line_buffer_ucs); log_ascii(reencoded_utf8); #endif // 0 OVR_ASSERT(check_equal(line_buffer_utf8, line_buffer_ucs)); OVR_ASSERT(check_equal(reencoded_utf8, line_buffer_ucs)); // Start next line. byte_counter = 0; } } fclose(fp); } return 0; } #endif // UTF8_UNIT_TEST }} // namespace UTF8Util::OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_UTF8Util.h b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_UTF8Util.h new file mode 100644 index 0000000..5153bef --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Kernel/OVR_UTF8Util.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_UTF8Util.h Content : UTF8 Unicode character encoding/decoding support Created : September 19, 2012 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_UTF8Util_h #define OVR_UTF8Util_h #include "OVR_Types.h" namespace OVR { namespace UTF8Util { //----------------------------------------------------------------------------------- // *** UTF8 string length and indexing. // Determines the length of UTF8 string in characters. // If source length is specified (in bytes), null 0 character is counted properly. SPInt OVR_STDCALL GetLength(const char* putf8str, SPInt length = -1); // Gets a decoded UTF8 character at index; you can access up to the index returned // by GetLength. 0 will be returned for out of bounds access. UInt32 OVR_STDCALL GetCharAt(SPInt index, const char* putf8str, SPInt length = -1); // Converts UTF8 character index into byte offset. // -1 is returned if index was out of bounds. SPInt OVR_STDCALL GetByteIndex(SPInt index, const char* putf8str, SPInt length = -1); // *** 16-bit Unicode string Encoding/Decoding routines. // Determines the number of bytes necessary to encode a string. // Does not count the terminating 0 (null) character. SPInt OVR_STDCALL GetEncodeStringSize(const wchar_t* pchar, SPInt length = -1); // Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at // least GetEncodeStringSize() + 1. void OVR_STDCALL EncodeString(char *pbuff, const wchar_t* pchar, SPInt length = -1); // Decode UTF8 into a wchar_t buffer. Must have GetLength()+1 characters available. // Characters over 0xFFFF are replaced with 0xFFFD. // Returns the length of resulting string (number of characters) UPInt OVR_STDCALL DecodeString(wchar_t *pbuff, const char* putf8str, SPInt bytesLen = -1); // *** Individual character Encoding/Decoding. // Determined the number of bytes necessary to encode a UCS character. int OVR_STDCALL GetEncodeCharSize(UInt32 ucsCharacter); // Encodes the given UCS character into the given UTF-8 buffer. // Writes the data starting at buffer[offset], and // increments offset by the number of bytes written. // May write up to 6 bytes, so make sure there's room in the buffer void OVR_STDCALL EncodeChar(char* pbuffer, SPInt* poffset, UInt32 ucsCharacter); // Return the next Unicode character in the UTF-8 encoded buffer. // Invalid UTF-8 sequences produce a U+FFFD character as output. // Advances *utf8_buffer past the character returned. Pointer advance // occurs even if the terminating 0 character is hit, since that allows // strings with middle '\0' characters to be supported. UInt32 OVR_STDCALL DecodeNextChar_Advance0(const char** putf8Buffer); // Safer version of DecodeNextChar, which doesn't advance pointer if // null character is hit. inline UInt32 DecodeNextChar(const char** putf8Buffer) { UInt32 ch = DecodeNextChar_Advance0(putf8Buffer); if (ch == 0) (*putf8Buffer)--; return ch; } }} // OVR::UTF8Util #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI.cpp new file mode 100644 index 0000000..8f5ec85 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_CAPI.cpp Content : Experimental simple C interface to the HMD - version 1. Created : November 30, 2013 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_CAPI.h" #include "Kernel/OVR_Timer.h" #include "Kernel/OVR_Math.h" #include "Kernel/OVR_System.h" #include "OVR_Stereo.h" #include "OVR_Profile.h" #include "CAPI/CAPI_GlobalState.h" #include "CAPI/CAPI_HMDState.h" #include "CAPI/CAPI_FrameTimeManager.h" using namespace OVR; using namespace OVR::Util::Render; //------------------------------------------------------------------------------------- // Math namespace OVR { // ***** FovPort // C-interop support: FovPort <-> ovrFovPort FovPort::FovPort(const ovrFovPort &src) : UpTan(src.UpTan), DownTan(src.DownTan), LeftTan(src.LeftTan), RightTan(src.RightTan) { } FovPort::operator ovrFovPort () const { ovrFovPort result; result.LeftTan = LeftTan; result.RightTan = RightTan; result.UpTan = UpTan; result.DownTan = DownTan; return result; } // Converts Fov Tan angle units to [-1,1] render target NDC space Vector2f FovPort::TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle) { ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this); return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset; } // ***** SensorState SensorState::SensorState(const ovrSensorState& s) { Predicted = s.Predicted; Recorded = s.Recorded; Temperature = s.Temperature; StatusFlags = s.StatusFlags; } SensorState::operator ovrSensorState() const { ovrSensorState result; result.Predicted = Predicted; result.Recorded = Recorded; result.Temperature = Temperature; result.StatusFlags = StatusFlags; return result; } } // namespace OVR //------------------------------------------------------------------------------------- using namespace OVR::CAPI; #ifdef __cplusplus extern "C" { #endif // Used to generate projection from ovrEyeDesc::Fov OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, ovrBool rightHanded) { return CreateProjection(rightHanded ? true : false, fov, znear, zfar); } OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale, float orthoDistance, float eyeViewAdjustX) { float orthoHorizontalOffset = eyeViewAdjustX / orthoDistance; // Current projection maps real-world vector (x,y,1) to the RT. // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to // the physical [-orthoHalfFov,orthoHalfFov] // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means // we don't have to feed in Z=1 all the time. // The horizontal offset math is a little hinky because the destination is // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset] // So we need to first map [-FovPixels/2,FovPixels/2] to // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]: // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset; // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset; // But then we need the sam mapping as the existing projection matrix, i.e. // x2 = x1 * Projection.M[0][0] + Projection.M[0][2]; // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2]; // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels + // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]; // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]. Matrix4f ortho; ortho.M[0][0] = projection.M[0][0] * orthoScale.x; ortho.M[0][1] = 0.0f; ortho.M[0][2] = 0.0f; ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] ); ortho.M[1][0] = 0.0f; ortho.M[1][1] = -projection.M[1][1] * orthoScale.y; // Note sign flip (text rendering uses Y=down). ortho.M[1][2] = 0.0f; ortho.M[1][3] = -projection.M[1][2]; /* if ( fabsf ( zNear - zFar ) < 0.001f ) { ortho.M[2][0] = 0.0f; ortho.M[2][1] = 0.0f; ortho.M[2][2] = 0.0f; ortho.M[2][3] = zFar; } else { ortho.M[2][0] = 0.0f; ortho.M[2][1] = 0.0f; ortho.M[2][2] = zFar / (zNear - zFar); ortho.M[2][3] = (zFar * zNear) / (zNear - zFar); } */ // MA: Undo effect of sign ortho.M[2][0] = 0.0f; ortho.M[2][1] = 0.0f; //ortho.M[2][2] = projection.M[2][2] * projection.M[3][2] * -1.0f; // reverse right-handedness ortho.M[2][2] = 0.0f; ortho.M[2][3] = 0.0f; //projection.M[2][3]; // No perspective correction for ortho. ortho.M[3][0] = 0.0f; ortho.M[3][1] = 0.0f; ortho.M[3][2] = 0.0f; ortho.M[3][3] = 1.0f; return ortho; } OVR_EXPORT double ovr_GetTimeInSeconds() { return Timer::GetSeconds(); } // Waits until the specified absolute time. OVR_EXPORT double ovr_WaitTillTime(double absTime) { volatile int i; double initialTime = ovr_GetTimeInSeconds(); double newTime = initialTime; while(newTime < absTime) { for (int j = 0; j < 50; j++) i = 0; newTime = ovr_GetTimeInSeconds(); } // How long we waited return newTime - initialTime; } //------------------------------------------------------------------------------------- // 1. Init/shutdown. static ovrBool CAPI_SystemInitCalled = 0; OVR_EXPORT ovrBool ovr_Initialize() { if (OVR::CAPI::GlobalState::pInstance) return 1; // We must set up the system for the plugin to work if (!OVR::System::IsInitialized()) { OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); CAPI_SystemInitCalled = 1; } // Constructor detects devices GlobalState::pInstance = new GlobalState; return 1; } OVR_EXPORT void ovr_Shutdown() { if (!GlobalState::pInstance) return; delete GlobalState::pInstance; GlobalState::pInstance = 0; // We should clean up the system to be complete if (CAPI_SystemInitCalled) { OVR::System::Destroy(); CAPI_SystemInitCalled = 0; } return; } // There is a thread safety issue with ovrHmd_Detect in that multiple calls from different // threads can corrupt the global array state. This would lead to two problems: // a) Create(index) enumerator may miss or overshoot items. Probably not a big deal // as game logic can easily be written to only do Detect(s)/Creates in one place. // The alternative would be to return list handle. // b) TBD: Un-mutexed Detect access from two threads could lead to crash. We should // probably check this. // OVR_EXPORT int ovrHmd_Detect() { if (!GlobalState::pInstance) return 0; return GlobalState::pInstance->EnumerateDevices(); } // ovrHmd_Create us explicitly separated from StartSensor and Configure to allow creation of // a relatively light-weight handle that would reference the device going forward and would // survive future ovrHmd_Detect calls. That is once ovrHMD is returned, index is no longer // necessary and can be changed by a ovrHmd_Detect call. OVR_EXPORT ovrHmd ovrHmd_Create(int index) { if (!GlobalState::pInstance) return 0; Ptr device = *GlobalState::pInstance->CreateDevice(index); if (!device) return 0; HMDState* hmds = new HMDState(device); if (!hmds) return 0; return hmds; } OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type) { if (!GlobalState::pInstance) return 0; HMDState* hmds = new HMDState(type); return hmds; } OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmd) { if (!hmd) return; // TBD: Any extra shutdown? HMDState* hmds = (HMDState*)hmd; { // Thread checker in its own scope, to avoid access after 'delete'. // Essentially just checks that no other RenderAPI function is executing. ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_Destroy"); } delete (HMDState*)hmd; } OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmd) { using namespace OVR; if (!hmd) { if (!GlobalState::pInstance) return "LibOVR not initialized."; return GlobalState::pInstance->GetLastError(); } HMDState* p = (HMDState*)hmd; return p->GetLastError(); } //------------------------------------------------------------------------------------- // Returns capability bits that are enabled at this time; described by ovrHmdCapBits. // Note that this value is different font ovrHmdDesc::Caps, which describes what // capabilities are available. OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmd) { HMDState* p = (HMDState*)hmd; return p ? p->EnabledHmdCaps : 0; } // Modifies capability bits described by ovrHmdCapBits that can be modified, // such as ovrHmd_LowPersistance. OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int capsBits) { HMDState* p = (HMDState*)hmd; if (p) p->SetEnabledHmdCaps(capsBits); } //------------------------------------------------------------------------------------- // *** Sensor // Sensor APIs are separated from Create & Configure for several reasons: // - They need custom parameters that control allocation of heavy resources // such as Vision tracking, which you don't want to create unless necessary. // - A game may want to switch some sensor settings based on user input, // or at lease enable/disable features such as Vision for debugging. // - The same or syntactically similar sensor interface is likely to be used if we // introduce controllers. // // - Sensor interface functions are all Thread-safe, unlike the frame/render API // functions that have different rules (all frame access functions // must be on render thread) OVR_EXPORT ovrBool ovrHmd_StartSensor(ovrHmd hmd, unsigned int supportedCaps, unsigned int requiredCaps) { HMDState* p = (HMDState*)hmd; // TBD: Decide if we null-check arguments. return p->StartSensor(supportedCaps, requiredCaps); } OVR_EXPORT void ovrHmd_StopSensor(ovrHmd hmd) { HMDState* p = (HMDState*)hmd; p->StopSensor(); } OVR_EXPORT void ovrHmd_ResetSensor(ovrHmd hmd) { HMDState* p = (HMDState*)hmd; p->ResetSensor(); } OVR_EXPORT ovrSensorState ovrHmd_GetSensorState(ovrHmd hmd, double absTime) { HMDState* p = (HMDState*)hmd; return p->PredictedSensorState(absTime); } // Returns information about a sensor. Only valid after SensorStart. OVR_EXPORT ovrBool ovrHmd_GetSensorDesc(ovrHmd hmd, ovrSensorDesc* descOut) { HMDState* p = (HMDState*)hmd; return p->GetSensorDesc(descOut) ? 1 : 0; } //------------------------------------------------------------------------------------- // *** General Setup OVR_EXPORT void ovrHmd_GetDesc(ovrHmd hmd, ovrHmdDesc* desc) { HMDState* hmds = (HMDState*)hmd; *desc = hmds->RenderState.GetDesc(); desc->Handle = hmd; } // Per HMD -> calculateIdealPixelSize OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel) { if (!hmd) return Sizei(0); HMDState* hmds = (HMDState*)hmd; return hmds->RenderState.GetFOVTextureSize(eye, fov, pixelsPerDisplayPixel); } //------------------------------------------------------------------------------------- OVR_EXPORT ovrBool ovrHmd_ConfigureRendering( ovrHmd hmd, const ovrRenderAPIConfig* apiConfig, unsigned int distortionCaps, const ovrFovPort eyeFovIn[2], ovrEyeRenderDesc eyeRenderDescOut[2] ) { if (!hmd) return 0; return ((HMDState*)hmd)->ConfigureRendering(eyeRenderDescOut, eyeFovIn, apiConfig, distortionCaps); } // TBD: MA - Deprecated, need alternative void ovrHmd_SetVsync(ovrHmd hmd, ovrBool vsync) { if (!hmd) return; return ((HMDState*)hmd)->TimeManager.SetVsync(vsync? true : false); } OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmd, unsigned int frameIndex) { HMDState* hmds = (HMDState*)hmd; if (!hmds) { ovrFrameTiming f; memset(&f, 0, sizeof(f)); return f; } // Check: Proper configure and threading state for the call. hmds->checkRenderingConfigured("ovrHmd_BeginFrame"); OVR_ASSERT_LOG(hmds->BeginFrameCalled == false, ("ovrHmd_BeginFrame called multiple times.")); ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_BeginFrame"); hmds->BeginFrameCalled = true; hmds->BeginFrameThreadId = OVR::GetCurrentThreadId(); return ovrHmd_BeginFrameTiming(hmd, frameIndex); } // Renders textures to frame buffer OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmd) { HMDState* hmds = (HMDState*)hmd; if (!hmds) return; // Debug state checks: Must be in BeginFrame, on the same thread. hmds->checkBeginFrameScope("ovrHmd_EndFrame"); ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame"); // TBD: Move directly into renderer bool dk2LatencyTest = (hmds->HMDInfo.HmdType == HmdType_DK2) && (hmds->EnabledHmdCaps & ovrHmdCap_LatencyTest); if (dk2LatencyTest) { hmds->LatencyTest2DrawColor[0] = hmds->TimeManager.GetFrameLatencyTestDrawColor(); hmds->LatencyTest2DrawColor[1] = hmds->LatencyTest2DrawColor[0]; hmds->LatencyTest2DrawColor[2] = hmds->LatencyTest2DrawColor[0]; } if (hmds->pRenderer) { hmds->pRenderer->SaveGraphicsState(); hmds->pRenderer->EndFrame(true, hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : NULL, // MA: Use this color since we are running DK2 test all the time. dk2LatencyTest ? hmds->LatencyTest2DrawColor : 0 //hmds->LatencyTest2Active ? hmds->LatencyTest2DrawColor : NULL ); hmds->pRenderer->RestoreGraphicsState(); } // Call after present ovrHmd_EndFrameTiming(hmd); if (dk2LatencyTest) { hmds->TimeManager.UpdateFrameLatencyTrackingAfterEndFrame( hmds->LatencyTest2DrawColor[0], hmds->LatencyUtil2.GetLocklessState()); } // Out of BeginFrame hmds->BeginFrameThreadId = 0; hmds->BeginFrameCalled = false; } OVR_EXPORT ovrPosef ovrHmd_BeginEyeRender(ovrHmd hmd, ovrEyeType eye) { HMDState* hmds = (HMDState*)hmd; if (!hmds) return ovrPosef(); return hmds->BeginEyeRender(eye); } OVR_EXPORT void ovrHmd_EndEyeRender(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture) { HMDState* hmds = (HMDState*)hmd; if (!hmds) return; hmds->EndEyeRender(eye, renderPose, eyeTexture); } //------------------------------------------------------------------------------------- // ***** Frame Timing logic OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex) { ovrFrameTiming f; memset(&f, 0, sizeof(f)); HMDState* hmds = (HMDState*)hmd; if (hmds) { FrameTimeManager::Timing frameTiming = hmds->TimeManager.GetFrameTiming(frameIndex); f.ThisFrameSeconds = frameTiming.ThisFrameTime; f.NextFrameSeconds = frameTiming.NextFrameTime; f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; f.ScanoutMidpointSeconds= frameTiming.MidpointTime; f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1]; // Compute DeltaSeconds. f.DeltaSeconds = (hmds->LastGetFrameTimeSeconds == 0.0f) ? 0.0f : (float) (f.ThisFrameSeconds - hmds->LastFrameTimeSeconds); hmds->LastGetFrameTimeSeconds = f.ThisFrameSeconds; if (f.DeltaSeconds > 1.0f) f.DeltaSeconds = 1.0f; } return f; } OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frameIndex) { ovrFrameTiming f; memset(&f, 0, sizeof(f)); HMDState* hmds = (HMDState*)hmd; if (!hmds) return f; // Check: Proper state for the call. OVR_ASSERT_LOG(hmds->BeginFrameTimingCalled == false, ("ovrHmd_BeginFrameTiming called multiple times.")); hmds->BeginFrameTimingCalled = true; double thisFrameTime = hmds->TimeManager.BeginFrame(frameIndex); const FrameTimeManager::Timing &frameTiming = hmds->TimeManager.GetFrameTiming(); f.ThisFrameSeconds = thisFrameTime; f.NextFrameSeconds = frameTiming.NextFrameTime; f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; f.ScanoutMidpointSeconds= frameTiming.MidpointTime; f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1]; // Compute DeltaSeconds. f.DeltaSeconds = (hmds->LastFrameTimeSeconds == 0.0f) ? 0.0f : (float) (thisFrameTime - hmds->LastFrameTimeSeconds); hmds->LastFrameTimeSeconds = thisFrameTime; if (f.DeltaSeconds > 1.0f) f.DeltaSeconds = 1.0f; return f; } OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmd) { HMDState* hmds = (HMDState*)hmd; if (!hmds) return; // Debug state checks: Must be in BeginFrameTiming, on the same thread. hmds->checkBeginFrameTimingScope("ovrHmd_EndTiming"); // MA TBD: Correct chek or not? // ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame"); hmds->TimeManager.EndFrame(); hmds->BeginFrameTimingCalled = false; } OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex) { HMDState* hmds = (HMDState*)hmd; if (!hmds) return; hmds->TimeManager.ResetFrameTiming(frameIndex, false, hmds->RenderingConfigured); hmds->LastFrameTimeSeconds = 0.0; hmds->LastGetFrameTimeSeconds = 0.0; } OVR_EXPORT ovrPosef ovrHmd_GetEyePose(ovrHmd hmd, ovrEyeType eye) { HMDState* hmds = (HMDState*)hmd; if (!hmds) return ovrPosef(); hmds->checkBeginFrameTimingScope("ovrHmd_GetEyePose"); return hmds->TimeManager.GetEyePredictionPose(hmd, eye); } OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]) { HMDState* hmds = (HMDState*)hmd; if (!hmd) return; // Debug checks: BeginFrame was called, on the same thread. hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices"); hmds->TimeManager.GetTimewarpMatrices(hmd, eye, renderPose, twmOut); /* // MA: Took this out because new latency test approach just sames // the sample times in FrameTimeManager. // TODO: if no timewarp, then test latency in begin eye render if (eye == 0) { hmds->ProcessLatencyTest2(hmds->LatencyTest2DrawColor, -1.0f); } */ } OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov) { ovrEyeRenderDesc erd; HMDState* hmds = (HMDState*)hmd; if (!hmds) { memset(&erd, 0, sizeof(erd)); return erd; } return hmds->RenderState.calcRenderDesc(eyeType, fov); } #define OVR_OFFSET_OF(s, field) ((size_t)&((s*)0)->field) // Generate distortion mesh per eye. // scaleAndOffsetOut - this will be needed for shader OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov, unsigned int distortionCaps, ovrDistortionMesh *meshData ) { 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_OS_WIN32) // TBD: We should probably be sharing some C API structures with C++ to avoid this mess... OVR_COMPILER_ASSERT(sizeof(DistortionMeshVertexData) == sizeof(ovrDistortionVertex)); OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, ScreenPosNDC) == OVR_OFFSET_OF(ovrDistortionVertex, Pos)); OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TimewarpLerp) == OVR_OFFSET_OF(ovrDistortionVertex, TimeWarpFactor)); OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, Shade) == OVR_OFFSET_OF(ovrDistortionVertex, VignetteFactor)); OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesR) == OVR_OFFSET_OF(ovrDistortionVertex, TexR)); OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesG) == OVR_OFFSET_OF(ovrDistortionVertex, TexG)); OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesB) == OVR_OFFSET_OF(ovrDistortionVertex, TexB)); #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; const DistortionRenderDesc& distortion = hmds->RenderState.Distortion[eyeType]; // 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**)&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; } // Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements // are set to null and 0s after the call. OVR_EXPORT void ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData) { if (meshData->pVertexData) DistortionMeshDestroy((DistortionMeshVertexData*)meshData->pVertexData, meshData->pIndexData); meshData->pVertexData = 0; meshData->pIndexData = 0; meshData->VertexCount = 0; meshData->IndexCount = 0; } // Computes updated 'uvScaleOffsetOut' to be used with a distortion if render target size or // viewport changes after the fact. This can be used to adjust render size every frame, if desired. OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov, ovrSizei textureSize, ovrRecti renderViewport, ovrVector2f uvScaleOffsetOut[2] ) { // Find the mapping from TanAngle space to target NDC space. ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov); // Find the mapping from TanAngle space to textureUV space. ScaleAndOffset2D eyeToSourceUV = CreateUVScaleAndOffsetfromNDCScaleandOffset( eyeToSourceNDC, renderViewport, textureSize ); uvScaleOffsetOut[0] = eyeToSourceUV.Scale; uvScaleOffsetOut[1] = eyeToSourceUV.Offset; } //------------------------------------------------------------------------------------- // ***** Latency Test interface OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmd, unsigned char rgbColorOut[3]) { HMDState* p = (HMDState*)hmd; rgbColorOut[0] = p->LatencyTestDrawColor[0]; rgbColorOut[1] = p->LatencyTestDrawColor[1]; rgbColorOut[2] = p->LatencyTestDrawColor[2]; return p->LatencyTestActive; } OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmd) { HMDState* p = (HMDState*)hmd; return p->LatencyUtil.GetResultsString(); } OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmd) { HMDState* p = (HMDState*)hmd; // MA Test float latencies[3]; p->TimeManager.GetLatencyTimings(latencies); return latencies[2]; // return p->LatencyUtil2.GetMeasuredLatency(); } // ----------------------------------------------------------------------------------- // ***** Property Access OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal) { HMDState* hmds = (HMDState*)hmd; if (hmds) { return hmds->getFloatValue(propertyName, defaultVal); } return defaultVal; } OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value) { HMDState* hmds = (HMDState*)hmd; if (hmds) { return hmds->setFloatValue(propertyName, value); } return false; } OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName, float values[], unsigned int arraySize) { HMDState* hmds = (HMDState*)hmd; if (hmds) { return hmds->getFloatArray(propertyName, values, arraySize); } return 0; } // Modify float[] property; false if property doesn't exist or is readonly. OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmd, const char* propertyName, float values[], unsigned int arraySize) { HMDState* hmds = (HMDState*)hmd; if (hmds) { return hmds->setFloatArray(propertyName, values, arraySize); } return 0; } OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmd, const char* propertyName, const char* defaultVal) { HMDState* hmds = (HMDState*)hmd; if (hmds) { return hmds->getString(propertyName, defaultVal); } return defaultVal; } /* Not needed yet. // Get array of strings, i.e. const char* [] property. // Returns the number of elements filled in, 0 if property doesn't exist. // Maximum of arraySize elements will be written. // String memory is guaranteed to exist until next call to GetString or GetStringArray, or HMD is destroyed. OVR_EXPORT unsigned int ovrHmd_GetStringArray(ovrHmd hmd, const char* propertyName, const char* values[], unsigned int arraySize) { HMDState* hmds = (HMDState*)hmd; if (hmds && hmds->pHMD && arraySize) { Profile* p = hmds->pHMD->GetProfile(); hmds->LastGetStringValue[0] = 0; if (p && p->GetValue(propertyName, hmds->LastGetStringValue, sizeof(hmds->LastGetStringValue))) { values[0] = hmds->LastGetStringValue; return 1; } } return 0; } */ // Returns array size of a property, 0 if property doesn't exist. // Can be used to check existence of a property. OVR_EXPORT unsigned int ovrHmd_GetArraySize(ovrHmd hmd, const char* propertyName) { HMDState* hmds = (HMDState*)hmd; if (hmds && hmds->pHMD) { // For now, just access the profile. Profile* p = hmds->pHMD->GetProfile(); if (p) return p->GetNumValues(propertyName); } return 0; } #ifdef __cplusplus } // extern "C" #endif //------------------------------------------------------------------------------------- // ****** Special access for VRConfig // Return the sensor fusion object for the purposes of magnetometer calibration. The // function is private and is only exposed through VRConfig header declarations OVR::SensorFusion* ovrHmd_GetSensorFusion(ovrHmd hmd) { HMDState* p = (HMDState*)hmd; return &p->SFusion; } \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI.h new file mode 100644 index 0000000..acef29c --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_CAPI.h Content : C Interface to Oculus sensors and rendering. Created : November 23, 2013 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_CAPI_h #define OVR_CAPI_h #include typedef char ovrBool; //----------------------------------------------------------------------------------- // ***** OVR_EXPORT definition #if !defined(OVR_EXPORT) #if defined(WIN32) #define OVR_EXPORT __declspec(dllexport) #else #define OVR_EXPORT #endif #endif //----------------------------------------------------------------------------------- // ***** Simple Math Structures // 2D integer typedef struct ovrVector2i_ { int x, y; } ovrVector2i; typedef struct ovrSizei_ { int w, h; } ovrSizei; typedef struct ovrRecti_ { ovrVector2i Pos; ovrSizei Size; } ovrRecti; // 3D typedef struct ovrQuatf_ { float x, y, z, w; } ovrQuatf; typedef struct ovrVector2f_ { float x, y; } ovrVector2f; typedef struct ovrVector3f_ { float x, y, z; } ovrVector3f; typedef struct ovrMatrix4f_ { float M[4][4]; } ovrMatrix4f; // Position and orientation together. typedef struct ovrPosef_ { ovrQuatf Orientation; ovrVector3f Position; } ovrPosef; // Full pose (rigid body) configuration with first and second derivatives. typedef struct ovrPoseStatef_ { ovrPosef Pose; ovrVector3f AngularVelocity; ovrVector3f LinearVelocity; ovrVector3f AngularAcceleration; ovrVector3f LinearAcceleration; double TimeInSeconds; // Absolute time of this state sample. } ovrPoseStatef; // Field Of View (FOV) in tangent of the angle units. // As an example, for a standard 90 degree vertical FOV, we would // have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }. typedef struct ovrFovPort_ { float UpTan; float DownTan; float LeftTan; float RightTan; } ovrFovPort; //----------------------------------------------------------------------------------- // ***** HMD Types // Enumerates all HMD types that we support. typedef enum { ovrHmd_None = 0, ovrHmd_DK1 = 3, ovrHmd_DKHD = 4, ovrHmd_CrystalCoveProto = 5, ovrHmd_DK2 = 6, ovrHmd_Other // Some HMD other then the one in the enumeration. } ovrHmdType; // HMD capability bits reported by device. // typedef enum { // Read-only flags. ovrHmdCap_Present = 0x0001, // This HMD exists (as opposed to being unplugged). ovrHmdCap_Available = 0x0002, // HMD and is sensor is available for use // (if not owned by another app). // These flags are intended for use with the new driver display mode. /* ovrHmdCap_ExtendDesktop = 0x0004, // Read only, means display driver is in compatibility mode. ovrHmdCap_DisplayOff = 0x0040, // Turns off Oculus HMD screen and output. ovrHmdCap_NoMirrorToWindow = 0x2000, // Disables mirrowing of HMD output to the window; // may improve rendering performance slightly. */ // Modifiable flags (through ovrHmd_SetEnabledCaps). ovrHmdCap_LowPersistence = 0x0080, // Supports low persistence mode. ovrHmdCap_LatencyTest = 0x0100, // Supports pixel reading for continuous latency testing. ovrHmdCap_DynamicPrediction = 0x0200, // Adjust prediction dynamically based on DK2 Latency. // Support rendering without VSync for debugging ovrHmdCap_NoVSync = 0x1000, ovrHmdCap_NoRestore = 0x4000, // These bits can be modified by ovrHmd_SetEnabledCaps. ovrHmdCap_Writable_Mask = 0x1380 } ovrHmdCaps; // Sensor capability bits reported by device. // Used with ovrHmd_StartSensor. typedef enum { ovrSensorCap_Orientation = 0x0010, // Supports orientation tracking (IMU). ovrSensorCap_YawCorrection = 0x0020, // Supports yaw correction through magnetometer or other means. ovrSensorCap_Position = 0x0040, // Supports positional tracking. } ovrSensorCaps; // Distortion capability bits reported by device. // Used with ovrHmd_ConfigureRendering and ovrHmd_CreateDistortionMesh. typedef enum { ovrDistortionCap_Chromatic = 0x01, // Supports chromatic aberration correction. ovrDistortionCap_TimeWarp = 0x02, // Supports timewarp. ovrDistortionCap_Vignette = 0x08 // Supports vignetting around the edges of the view. } ovrDistortionCaps; // Specifies which eye is being used for rendering. // This type explicitly does not include a third "NoStereo" option, as such is // not required for an HMD-centered API. typedef enum { ovrEye_Left = 0, ovrEye_Right = 1, ovrEye_Count = 2 } ovrEyeType; // Handle to HMD; returned by ovrHmd_Create. typedef struct ovrHmdStruct* ovrHmd; // This is a complete descriptor of the HMD. typedef struct ovrHmdDesc_ { ovrHmd Handle; // Handle of this HMD. ovrHmdType Type; // Name string describing the product: "Oculus Rift DK1", etc. const char* ProductName; const char* Manufacturer; // Capability bits described by ovrHmdCaps. unsigned int HmdCaps; // Capability bits described by ovrSensorCaps. unsigned int SensorCaps; // Capability bits described by ovrDistortionCaps. unsigned int DistortionCaps; // Resolution of the entire HMD screen (for both eyes) in pixels. ovrSizei Resolution; // Where monitor window should be on screen or (0,0). ovrVector2i WindowsPos; // These define the recommended and maximum optical FOVs for the HMD. ovrFovPort DefaultEyeFov[ovrEye_Count]; ovrFovPort MaxEyeFov[ovrEye_Count]; // Preferred eye rendering order for best performance. // Can help reduce latency on sideways-scanned screens. ovrEyeType EyeRenderOrder[ovrEye_Count]; // Display that HMD should present on. // TBD: It may be good to remove this information relying on WidowPos instead. // Ultimately, we may need to come up with a more convenient alternative, // such as a API-specific functions that return adapter, ot something that will // work with our monitor driver. // Windows: "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC. const char* DisplayDeviceName; // MacOS int DisplayId; } ovrHmdDesc; // Describes the type of positional tracking being done. /* typedef enum { ovrPose_None, ovrPose_HeadModel, ovrPose_Positional } ovrPoseType; */ // Bit flags describing the current status of sensor tracking. typedef enum { ovrStatus_OrientationTracked = 0x0001, // Orientation is currently tracked (connected and in use). ovrStatus_PositionTracked = 0x0002, // Position is currently tracked (FALSE if out of range). ovrStatus_PositionConnected = 0x0020, // Position tracking HW is connected. ovrStatus_HmdConnected = 0x0080 // HMD Display is available & connected. } ovrStatusBits; // State of the sensor at a given absolute time. typedef struct ovrSensorState_ { // Predicted pose configuration at requested absolute time. // One can determine the time difference between predicted and actual // readings by comparing ovrPoseState.TimeInSeconds. ovrPoseStatef Predicted; // Actual recorded pose configuration based on the sensor sample at a // moment closest to the requested time. ovrPoseStatef Recorded; // Sensor temperature reading, in degrees Celsius, as sample time. float Temperature; // Sensor status described by ovrStatusBits. unsigned int StatusFlags; } ovrSensorState; // For now. // TBD: Decide if this becomes a part of HMDDesc typedef struct ovrSensorDesc_ { // HID Vendor and ProductId of the device. short VendorId; short ProductId; // Sensor (and display) serial number. char SerialNumber[24]; } ovrSensorDesc; // Frame data reported by ovrHmd_BeginFrameTiming(). typedef struct ovrFrameTiming_ { // The amount of time that has passed since the previous frame returned // BeginFrameSeconds value, usable for movement scaling. // This will be clamped to no more than 0.1 seconds to prevent // excessive movement after pauses for loading or initialization. float DeltaSeconds; // It is generally expected that the following hold: // ThisFrameSeconds < TimewarpPointSeconds < NextFrameSeconds < // EyeScanoutSeconds[EyeOrder[0]] <= ScanoutMidpointSeconds <= EyeScanoutSeconds[EyeOrder[1]] // Absolute time value of when rendering of this frame began or is expected to // begin; generally equal to NextFrameSeconds of the previous frame. Can be used // for animation timing. double ThisFrameSeconds; // Absolute point when IMU expects to be sampled for this frame. double TimewarpPointSeconds; // Absolute time when frame Present + GPU Flush will finish, and the next frame starts. double NextFrameSeconds; // Time when when half of the screen will be scanned out. Can be passes as a prediction // value to ovrHmd_GetSensorState() go get general orientation. double ScanoutMidpointSeconds; // Timing points when each eye will be scanned out to display. Used for rendering each eye. double EyeScanoutSeconds[2]; } ovrFrameTiming; // Rendering information for each eye, computed by either ovrHmd_ConfigureRendering(). // or ovrHmd_GetRenderDesc() based on the specified Fov. // Note that the rendering viewport is not included here as it can be // specified separately and modified per frame though: // (a) calling ovrHmd_GetRenderScaleAndOffset with game-rendered api, // or (b) passing different values in ovrTexture in case of SDK-rendered distortion. typedef struct ovrEyeRenderDesc_ { ovrEyeType Eye; ovrFovPort Fov; ovrRecti DistortedViewport; // Distortion viewport ovrVector2f PixelsPerTanAngleAtCenter; // How many display pixels will fit in tan(angle) = 1. ovrVector3f ViewAdjust; // Translation to be applied to view matrix. } ovrEyeRenderDesc; //----------------------------------------------------------------------------------- // ***** Platform-independent Rendering Configuration // These types are used to hide platform-specific details when passing // render device, OS and texture data to the APIs. // // The benefit of having these wrappers vs. platform-specific API functions is // that they allow game glue code to be portable. A typical example is an // engine that has multiple back ends, say GL and D3D. Portable code that calls // these back ends may also use LibOVR. To do this, back ends can be modified // to return portable types such as ovrTexture and ovrRenderAPIConfig. typedef enum { ovrRenderAPI_None, ovrRenderAPI_OpenGL, ovrRenderAPI_Android_GLES, // May include extra native window pointers, etc. ovrRenderAPI_D3D9, ovrRenderAPI_D3D10, ovrRenderAPI_D3D11, ovrRenderAPI_Count } ovrRenderAPIType; // Platform-independent part of rendering API-configuration data. // It is a part of ovrRenderAPIConfig, passed to ovrHmd_Configure. typedef struct ovrRenderAPIConfigHeader_ { ovrRenderAPIType API; ovrSizei RTSize; int Multisample; } ovrRenderAPIConfigHeader; typedef struct ovrRenderAPIConfig_ { ovrRenderAPIConfigHeader Header; uintptr_t PlatformData[8]; } ovrRenderAPIConfig; // Platform-independent part of eye texture descriptor. // It is a part of ovrTexture, passed to ovrHmd_EndFrame. // - If RenderViewport is all zeros, will be used. typedef struct ovrTextureHeader_ { ovrRenderAPIType API; ovrSizei TextureSize; ovrRecti RenderViewport; // Pixel viewport in texture that holds eye image. } ovrTextureHeader; typedef struct ovrTexture_ { ovrTextureHeader Header; uintptr_t PlatformData[8]; } ovrTexture; // ----------------------------------------------------------------------------------- // ***** API Interfaces // Basic steps to use the API: // // Setup: // 1. ovrInitialize(); // 2. ovrHMD hmd = ovrHmd_Create(0); ovrHmd_GetDesc(hmd, &hmdDesc); // 3. Use hmdDesc and ovrHmd_GetFovTextureSize() to determine graphics configuration. // 4. Call ovrHmd_StartSensor() to configure and initialize tracking. // 5. Call ovrHmd_ConfigureRendering() to setup graphics for SDK rendering, // which is the preferred approach. // Please refer to "Game-Side Rendering" below if you prefer to do that instead. // 5. Allocate textures as needed. // // Game Loop: // 6. Call ovrHmd_BeginFrame() to get frame timing and orientation information. // 7. Render each eye in between ovrHmd_BeginEyeRender and ovrHmd_EndEyeRender calls, // providing the result texture to the API. // 8. Call ovrHmd_EndFrame() to render distorted textures to the back buffer // and present them on the Hmd. // // Shutdown: // 9. ovrHmd_Destroy(hmd) // 10. ovr_Shutdown() // #ifdef __cplusplus extern "C" { #endif // Library init/shutdown, must be called around all other OVR code. // No other functions calls are allowed before ovr_Initialize succeeds or after ovr_Shutdown. OVR_EXPORT ovrBool ovr_Initialize(); OVR_EXPORT void ovr_Shutdown(); // Detects or re-detects HMDs and reports the total number detected. // Users can get information about each HMD by calling ovrHmd_Create with an index. OVR_EXPORT int ovrHmd_Detect(); // Creates a handle to an HMD and optionally fills in data about it. // Index can [0 .. ovrHmd_Detect()-1]; index mappings can cange after each ovrHmd_Detect call. // If not null, returned handle must be freed with ovrHmd_Destroy. OVR_EXPORT ovrHmd ovrHmd_Create(int index); OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmd); // Creates a "fake" HMD used for debugging only. This is not tied to specific hardware, // but may be used to debug some of the related rendering. OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type); // Returns last error for HMD state. Returns null for no error. // String is valid until next call or GetLastError or HMD is destroyed. // Pass null hmd to get global error (for create, etc). OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmd); //------------------------------------------------------------------------------------- // Returns capability bits that are enabled at this time; described by ovrHmdCaps. // Note that this value is different font ovrHmdDesc::HmdCaps, which describes what // capabilities are available. OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmd); // Modifies capability bits described by ovrHmdCaps that can be modified, // such as ovrHmd_LowPersistance. OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int hmdCaps); //------------------------------------------------------------------------------------- // ***** Sensor Interface // All sensor interface functions are thread-safe, allowing sensor state to be sampled // from different threads. // Starts sensor sampling, enabling specified capabilities, described by ovrSensorCaps. // - supportedSensorCaps specifies support that is requested. The function will succeed // even if these caps are not available (i.e. sensor or camera is unplugged). Support // will automatically be enabled if such device is plugged in later. Software should // check ovrSensorState.StatusFlags for real-time status. // - requiredSensorCaps specify sensor capabilities required at the time of the call. // If they are not available, the function will fail. Pass 0 if only specifying // supportedSensorCaps. OVR_EXPORT ovrBool ovrHmd_StartSensor(ovrHmd hmd, unsigned int supportedSensorCaps, unsigned int requiredSensorCaps); // Stops sensor sampling, shutting down internal resources. OVR_EXPORT void ovrHmd_StopSensor(ovrHmd hmd); // Resets sensor orientation. OVR_EXPORT void ovrHmd_ResetSensor(ovrHmd hmd); // Returns sensor state reading based on the specified absolute system time. // Pass absTime value of 0.0 to request the most recent sensor reading; in this case // both PredictedPose and SamplePose will have the same value. // ovrHmd_GetEyePredictedSensorState relies on this internally. // This may also be used for more refined timing of FrontBuffer rendering logic, etc. OVR_EXPORT ovrSensorState ovrHmd_GetSensorState(ovrHmd hmd, double absTime); // Returns information about a sensor. // Only valid after StartSensor. OVR_EXPORT ovrBool ovrHmd_GetSensorDesc(ovrHmd hmd, ovrSensorDesc* descOut); //------------------------------------------------------------------------------------- // ***** Graphics Setup // Fills in description about HMD; this is the same as filled in by ovrHmd_Create. OVR_EXPORT void ovrHmd_GetDesc(ovrHmd hmd, ovrHmdDesc* desc); // Calculates texture size recommended for rendering one eye within HMD, given FOV cone. // Higher FOV will generally require larger textures to maintain quality. // - pixelsPerDisplayPixel specifies that number of render target pixels per display // pixel at center of distortion; 1.0 is the default value. Lower values // can improve performance. OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, float pixelsPerDisplayPixel); //------------------------------------------------------------------------------------- // ***** Rendering API Thread Safety // All of rendering APIs, inclusing Configure and frame functions are *NOT // Thread Safe*. It is ok to use ConfigureRendering on one thread and handle // frames on another thread, but explicit synchronization must be done since // functions that depend on configured state are not reentrant. // // As an extra requirement, any of the following calls must be done on // the render thread, which is the same thread that calls ovrHmd_BeginFrame // or ovrHmd_BeginFrameTiming. // - ovrHmd_EndFrame // - ovrHmd_BeginEyeRender // - ovrHmd_EndEyeRender // - ovrHmd_GetFramePointTime // - ovrHmd_GetEyePose // - ovrHmd_GetEyeTimewarpMatrices //------------------------------------------------------------------------------------- // ***** SDK-Rendering Functions // These functions support rendering of distortion by the SDK through direct // access to the underlying rendering HW, such as D3D or GL. // This is the recommended approach, as it allows for better support or future // Oculus hardware and a range of low-level optimizations. // Configures rendering; fills in computed render parameters. // This function can be called multiple times to change rendering settings. // The users pass in two eye view descriptors that are used to // generate complete rendering information for each eye in eyeRenderDescOut[2]. // // - apiConfig provides D3D/OpenGL specific parameters. Pass null // to shutdown rendering and release all resources. // - distortionCaps describe distortion settings that will be applied. // OVR_EXPORT ovrBool ovrHmd_ConfigureRendering( ovrHmd hmd, const ovrRenderAPIConfig* apiConfig, unsigned int distortionCaps, const ovrFovPort eyeFovIn[2], ovrEyeRenderDesc eyeRenderDescOut[2] ); // Begins a frame, returning timing and orientation information useful for simulation. // This should be called in the beginning of game rendering loop (on render thread). // This function relies on ovrHmd_BeginFrameTiming for some of its functionality. // Pass 0 for frame index if not using GetFrameTiming. OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmd, unsigned int frameIndex); // Ends frame, rendering textures to frame buffer. This may perform distortion and scaling // internally, assuming is it not delegated to another thread. // Must be called on the same thread as BeginFrame. Calls ovrHmd_BeginEndTiming internally. // *** This Function will to Present/SwapBuffers and potentially wait for GPU Sync ***. OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmd); // Marks beginning of eye rendering. Must be called on the same thread as BeginFrame. // This function uses ovrHmd_GetEyePose to predict sensor state that should be // used rendering the specified eye. // This combines current absolute time with prediction that is appropriate for this HMD. // It is ok to call ovrHmd_BeginEyeRender() on both eyes before calling ovrHmd_EndEyeRender. // If rendering one eye at a time, it is best to render eye specified by // HmdDesc.EyeRenderOrder[0] first. OVR_EXPORT ovrPosef ovrHmd_BeginEyeRender(ovrHmd hmd, ovrEyeType eye); // Marks the end of eye rendering and submits the eye texture for display after it is ready. // Rendering viewport within the texture can change per frame if necessary. // Specified texture may be presented immediately or wait until ovrHmd_EndFrame based // on the implementation. The API performs distortion and scaling internally. // 'renderPose' will typically be the value returned from ovrHmd_BeginEyeRender, but can // be different if a different pose was used for rendering. OVR_EXPORT void ovrHmd_EndEyeRender(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrTexture* eyeTexture); //------------------------------------------------------------------------------------- // ***** Game-Side Rendering Functions // These functions provide distortion data and render timing support necessary to allow // game rendering of distortion. Game-side rendering involves the following steps: // // 1. Setup ovrEyeDesc based on desired texture size and Fov. // Call ovrHmd_GetRenderDesc to get the necessary rendering parameters for each eye. // // 2. Use ovrHmd_CreateDistortionMesh to generate distortion mesh. // // 3. Use ovrHmd_BeginFrameTiming, ovrHmd_GetEyePose and ovrHmd_BeginFrameTiming // in the rendering loop to obtain timing and predicted view orientation for // each eye. // - If relying on timewarp, use ovr_WaitTillTime after rendering+flush, followed // by ovrHmd_GetEyeTimewarpMatrices to obtain timewarp matrices used // in distortion pixel shader to reduce latency. // // Computes distortion viewport, view adjust and other rendering for the specified // eye. This can be used instead of ovrHmd_ConfigureRendering to help setup rendering on // the game side. OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov); // Describes a vertex used for distortion; this is intended to be converted into // the engine-specific format. // Some fields may be unused based on ovrDistortionCaps selected. TexG and TexB, for example, // are not used if chromatic correction is not requested. typedef struct ovrDistortionVertex_ { ovrVector2f Pos; float TimeWarpFactor; // Lerp factor between time-warp matrices. Can be encoded in Pos.z. float VignetteFactor; // Vignette fade factor. Can be encoded in Pos.w. ovrVector2f TexR; ovrVector2f TexG; ovrVector2f TexB; } ovrDistortionVertex; // Describes a full set of distortion mesh data, filled in by ovrHmd_CreateDistortionMesh. // Contents of this data structure, if not null, should be freed by ovrHmd_DestroyDistortionMesh. typedef struct ovrDistortionMesh_ { ovrDistortionVertex* pVertexData; unsigned short* pIndexData; unsigned int VertexCount; unsigned int IndexCount; } ovrDistortionMesh; // Generate distortion mesh per eye. // Distortion capabilities will depend on 'distortionCaps' flags; user should rely on // appropriate shaders based on their settings. // Distortion mesh data will be allocated and stored into the ovrDistortionMesh data structure, // which should be explicitly freed with ovrHmd_DestroyDistortionMesh. // Users should call ovrHmd_GetRenderScaleAndOffset to get uvScale and Offset values for rendering. // The function shouldn't fail unless theres is a configuration or memory error, in which case // ovrDistortionMesh values will be set to null. OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmd, ovrEyeType eyeType, ovrFovPort fov, unsigned int distortionCaps, ovrDistortionMesh *meshData ); // Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements // are set to null and zeroes after the call. OVR_EXPORT void ovrHmd_DestroyDistortionMesh( ovrDistortionMesh* meshData ); // Computes updated 'uvScaleOffsetOut' to be used with a distortion if render target size or // viewport changes after the fact. This can be used to adjust render size every frame, if desired. OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov, ovrSizei textureSize, ovrRecti renderViewport, ovrVector2f uvScaleOffsetOut[2] ); // Thread-safe timing function for the main thread. Caller should increment frameIndex // with every frame and pass the index to RenderThread for processing. OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex); // Called at the beginning of the frame on the Render Thread. // Pass frameIndex == 0 if ovrHmd_GetFrameTiming isn't being used. Otherwise, // pass the same frame index as was used for GetFrameTiming on the main thread. OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frameIndex); // Marks the end of game-rendered frame, tracking the necessary timing information. This // function must be called immediately after Present/SwapBuffers + GPU sync. GPU sync is important // before this call to reduce latency and ensure proper timing. OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmd); // Initializes and resets frame time tracking. This is typically not necessary, but // is helpful if game changes vsync state or video mode. vsync is assumed to be on if this // isn't called. Resets internal frame index to the specified number. OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex); // Predicts and returns Pose that should be used rendering the specified eye. // Must be called between ovrHmd_BeginFrameTiming & ovrHmd_EndFrameTiming. OVR_EXPORT ovrPosef ovrHmd_GetEyePose(ovrHmd hmd, ovrEyeType eye); // Computes timewarp matrices used by distortion mesh shader, these are used to adjust // for orientation change since the last call to ovrHmd_GetEyePose for this eye. // The ovrDistortionVertex::TimeWarpFactor is used to blend between the matrices, // usually representing two different sides of the screen. // Must be called on the same thread as ovrHmd_BeginFrameTiming. OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, ovrPosef renderPose, ovrMatrix4f twmOut[2]); //------------------------------------------------------------------------------------- // ***** Stateless math setup functions // Used to generate projection from ovrEyeDesc::Fov. OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection( ovrFovPort fov, float znear, float zfar, ovrBool rightHanded ); // Used for 2D rendering, Y is down // orthoScale = 1.0f / pixelsPerTanAngleAtCenter // orthoDistance = distance from camera, such as 0.8m OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale, float orthoDistance, float eyeViewAdjustX); // Returns global, absolute high-resolution time in seconds. This is the same // value as used in sensor messages. OVR_EXPORT double ovr_GetTimeInSeconds(); // Waits until the specified absolute time. OVR_EXPORT double ovr_WaitTillTime(double absTime); // ----------------------------------------------------------------------------------- // ***** Latency Test interface // Does latency test processing and returns 'TRUE' if specified rgb color should // be used to clear the screen. OVR_EXPORT ovrBool ovrHmd_ProcessLatencyTest(ovrHmd hmd, unsigned char rgbColorOut[3]); // Returns non-null string once with latency test result, when it is available. // Buffer is valid until next call. OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmd); // Returns latency for HMDs that support internal latency testing via the // pixel-read back method (-1 for invalid or N/A) OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmd); // ----------------------------------------------------------------------------------- // ***** Property Access // NOTICE: This is experimental part of API that is likely to go away or change. // These allow accessing different properties of the HMD and profile. // Some of the properties may go away with profile/HMD versions, so software should // use defaults and/or proper fallbacks. // // For now, access profile entries; this will change. #if !defined(OVR_KEY_USER) #define OVR_KEY_USER "User" #define OVR_KEY_NAME "Name" #define OVR_KEY_GENDER "Gender" #define OVR_KEY_PLAYER_HEIGHT "PlayerHeight" #define OVR_KEY_EYE_HEIGHT "EyeHeight" #define OVR_KEY_IPD "IPD" #define OVR_KEY_NECK_TO_EYE_HORIZONTAL "NeckEyeHori" #define OVR_KEY_NECK_TO_EYE_VERTICAL "NeckEyeVert" #define OVR_DEFAULT_GENDER "Male" #define OVR_DEFAULT_PLAYER_HEIGHT 1.778f #define OVR_DEFAULT_EYE_HEIGHT 1.675f #define OVR_DEFAULT_IPD 0.064f #define OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL 0.12f #define OVR_DEFAULT_NECK_TO_EYE_VERTICAL 0.12f #endif // Get float property. Returns first element if property is a float array. // Returns defaultValue if property doesn't exist. OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal); // Modify float property; false if property doesn't exist or is readonly. OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value); // Get float[] property. Returns the number of elements filled in, 0 if property doesn't exist. // Maximum of arraySize elements will be written. OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName, float values[], unsigned int arraySize); // Modify float[] property; false if property doesn't exist or is readonly. OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmd, const char* propertyName, float values[], unsigned int arraySize); // Get string property. Returns first element if property is a string array. // Returns defaultValue if property doesn't exist. // String memory is guaranteed to exist until next call to GetString or GetStringArray, or HMD is destroyed. OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmd, const char* propertyName, const char* defaultVal); // Returns array size of a property, 0 if property doesn't exist. // Can be used to check existence of a property. OVR_EXPORT unsigned int ovrHmd_GetArraySize(ovrHmd hmd, const char* propertyName); #ifdef __cplusplus } // extern "C" #endif #endif // OVR_CAPI_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI_GL.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI_GL.h new file mode 100644 index 0000000..ac136b7 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_CAPI_GL.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_CAPI_GL.h Content : GL specific structures used by the CAPI interface. Created : November 7, 2013 Authors : Lee Cooper Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved. Use of this software is subject to the terms of the Oculus Inc license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. ************************************************************************************/ #ifndef OVR_CAPI_GL_h #define OVR_CAPI_GL_h #include "OVR_CAPI.h" //----------------------------------------------------------------------------------- // ***** GL Specific #if defined(OVR_OS_WIN32) #include #include #include #include #elif defined(OVR_OS_MAC) #include #include #include #else #include #include #include #endif // Used to configure slave GL rendering (i.e. for devices created externally). typedef struct ovrGLConfigData_s { // General device settings. ovrRenderAPIConfigHeader Header; #if defined(OVR_OS_WIN32) HWND Window; #elif defined(OVR_OS_LINUX) Display* Disp; Window Win; #endif } ovrGLConfigData; union ovrGLConfig { ovrRenderAPIConfig Config; ovrGLConfigData OGL; }; // Used to pass GL eye texture data to ovrHmd_EndFrame. typedef struct ovrGLTextureData_s { // General device settings. ovrTextureHeader Header; GLuint TexId; } ovrGLTextureData; typedef union ovrGLTexture_s { ovrTexture Texture; ovrGLTextureData OGL; } ovrGLTexture; #endif // OVR_CAPI_GL_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Common_HMDDevice.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Common_HMDDevice.cpp new file mode 100644 index 0000000..4461eee --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Common_HMDDevice.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Common_HMDDevice.cpp Content : Created : Authors : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ // Should be #included from the relevant OVR_YourPlatformHere_HMDDevice.cpp #include "Kernel/OVR_Alg.h" //------------------------------------------------------------------------------------- // ***** HMDDeviceCreateDesc DeviceBase* HMDDeviceCreateDesc::NewDeviceInstance() { return new HMDDevice(this); } void HMDDeviceCreateDesc::SetScreenParameters(int x, int y, int hres, int vres, float hsize, float vsize, float vCenterFromTopInMeters, float lensSeparationInMeters) { Desktop.X = x; Desktop.Y = y; ResolutionInPixels = Sizei(hres, vres); ScreenSizeInMeters = Sizef(hsize, vsize); VCenterFromTopInMeters = vCenterFromTopInMeters; LensSeparationInMeters = lensSeparationInMeters; Contents |= Contents_Screen; } void HMDDeviceCreateDesc::SetDistortion(const float* dks) { for (int i = 0; i < 4; i++) DistortionK[i] = dks[i]; // TODO: add DistortionEqn Contents |= Contents_Distortion; } HmdTypeEnum HMDDeviceCreateDesc::GetHmdType() const { // Determine the HMD model // The closest thing we have to a dependable model indicator are the // the screen characteristics. Additionally we can check the sensor // (on attached devices) to further refine our guess HmdTypeEnum hmdType = HmdType_Unknown; if ( ResolutionInPixels.w == 1280 ) { if ( ScreenSizeInMeters.w > 0.1497f && ScreenSizeInMeters.w < 0.1498f ) hmdType = HmdType_DK1; else hmdType = HmdType_DKProto; } else if ( ResolutionInPixels.w == 1920 ) { // DKHD protoypes, all 1920x1080 if ( ScreenSizeInMeters.w > 0.1209f && ScreenSizeInMeters.w < 0.1210f ) { // Screen size 0.12096 x 0.06804 hmdType = HmdType_DKHDProto; } else if ( ScreenSizeInMeters.w > 0.1257f && ScreenSizeInMeters.w < 0.1258f ) { // Screen size 0.125 x 0.071 // Could be a HmdType_DKHDProto566Mi, HmdType_CrystalCoveProto, or DK2 // - most likely the latter. hmdType = HmdType_DK2; // If available, check the sensor to determine exactly which variant this is if (pDevice) { Ptr sensor = *((HMDDevice*)pDevice)->GetSensor(); SensorInfo sinfo; if (sensor && sensor->GetDeviceInfo(&sinfo)) { if (sinfo.ProductId == 1) { hmdType = HmdType_DKHDProto566Mi; } else { // Crystal Cove uses 0.# firmware, DK2 uses 1.# int firm_major = Alg::DecodeBCD((sinfo.Version >> 8) & 0x00ff); int firm_minor = Alg::DecodeBCD(sinfo.Version & 0xff); OVR_UNUSED(firm_minor); if (firm_major == 0) hmdType = HmdType_CrystalCoveProto; else hmdType = HmdType_DK2; } } } } else if (ScreenSizeInMeters.w > 0.1295f && ScreenSizeInMeters.w < 0.1297f) { // Screen size 0.1296 x 0.0729 hmdType = HmdType_DKHD2Proto; } } OVR_ASSERT( hmdType != HmdType_Unknown ); return hmdType; } bool HMDDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const { if ((info->InfoClassType != Device_HMD) && (info->InfoClassType != Device_None)) return false; HmdTypeEnum hmdType = GetHmdType(); char const* deviceName = "Oculus HMD"; switch (hmdType) { case HmdType_DKProto: deviceName = "Oculus Rift Prototype"; break; case HmdType_DK1: deviceName = "Oculus Rift DK1"; break; case HmdType_DKHDProto: deviceName = "Oculus Rift DKHD"; break; case HmdType_DKHD2Proto: deviceName = "Oculus Rift DKHD2"; break; case HmdType_DKHDProto566Mi: deviceName = "Oculus Rift DKHD 566 Mi"; break; case HmdType_CrystalCoveProto: deviceName = "Oculus Rift Crystal Cove"; break; case HmdType_DK2: deviceName = "Oculus Rift DK2"; break; default: deviceName = "Oculus HMD"; break; } info->ProductName = deviceName; info->Manufacturer = "Oculus VR"; info->Type = Device_HMD; info->Version = 0; // Display detection. if (info->InfoClassType == Device_HMD) { HMDInfo* hmdInfo = static_cast(info); hmdInfo->HmdType = hmdType; hmdInfo->DesktopX = Desktop.X; hmdInfo->DesktopY = Desktop.Y; hmdInfo->ResolutionInPixels = ResolutionInPixels; hmdInfo->ScreenSizeInMeters = ScreenSizeInMeters; // Includes ScreenGapSizeInMeters hmdInfo->ScreenGapSizeInMeters = 0.0f; hmdInfo->CenterFromTopInMeters = VCenterFromTopInMeters; hmdInfo->LensSeparationInMeters = LensSeparationInMeters; // TODO: any other information we get from the hardware itself should be added to this list switch ( hmdInfo->HmdType ) { case HmdType_DKProto: // WARNING - estimated. hmdInfo->Shutter.Type = HmdShutter_RollingTopToBottom; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.000052f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.016580f; hmdInfo->Shutter.PixelSettleTime = 0.015f; // estimated. hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence break; case HmdType_DK1: // Data from specs. hmdInfo->Shutter.Type = HmdShutter_RollingTopToBottom; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.00018226f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.01620089f; hmdInfo->Shutter.PixelSettleTime = 0.017f; // estimated. hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence break; case HmdType_DKHDProto: // Data from specs. hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.0000859f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0164948f; hmdInfo->Shutter.PixelSettleTime = 0.012f; // estimated. hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence break; case HmdType_DKHD2Proto: // Data from specs. hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 60.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.000052f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.016580f; hmdInfo->Shutter.PixelSettleTime = 0.015f; // estimated. hmdInfo->Shutter.PixelPersistence = hmdInfo->Shutter.VsyncToNextVsync; // Full persistence break; case HmdType_DKHDProto566Mi: #if 0 // Low-persistence global shutter hmdInfo->Shutter.Type = HmdShutter_Global; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f + 0.0131033f; // Global shutter - first visible scan line is actually the last! hmdInfo->Shutter.FirstScanlineToLastScanline = 0.000f; // Global shutter - all visible at once. hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame. #else // Low-persistence rolling shutter hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0131033f; hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame. #endif break; case HmdType_CrystalCoveProto: // Low-persistence rolling shutter hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0131033f; hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame. break; case HmdType_DK2: // Low-persistence rolling shutter hmdInfo->Shutter.Type = HmdShutter_RollingRightToLeft; hmdInfo->Shutter.VsyncToNextVsync = ( 1.0f / 76.0f ); hmdInfo->Shutter.VsyncToFirstScanline = 0.0000273f; hmdInfo->Shutter.FirstScanlineToLastScanline = 0.0131033f; hmdInfo->Shutter.PixelSettleTime = 0.0f; // <100us hmdInfo->Shutter.PixelPersistence = 0.18f * hmdInfo->Shutter.VsyncToNextVsync; // Confgurable - currently set to 18% of total frame. break; default: OVR_ASSERT ( false ); break; } OVR_strcpy(hmdInfo->DisplayDeviceName, sizeof(hmdInfo->DisplayDeviceName), DisplayDeviceName.ToCStr()); #if defined(OVR_OS_WIN32) // Nothing special for Win32. #elif defined(OVR_OS_MAC) hmdInfo->DisplayId = DisplayId; #elif defined(OVR_OS_LINUX) hmdInfo->DisplayId = DisplayId; #elif defined(OVR_OS_ANDROID) hmdInfo->DisplayId = DisplayId; #else #error Unknown platform #endif } return true; } //------------------------------------------------------------------------------------- // ***** HMDDevice HMDDevice::HMDDevice(HMDDeviceCreateDesc* createDesc) : OVR::DeviceImpl(createDesc, 0) { } HMDDevice::~HMDDevice() { } bool HMDDevice::Initialize(DeviceBase* parent) { pParent = parent; return true; } void HMDDevice::Shutdown() { ProfileName.Clear(); pCachedProfile.Clear(); pParent.Clear(); } Profile* HMDDevice::GetProfile() { // Loads and returns a cached profile based on this device and current user if (pCachedProfile == NULL) { ProfileManager* mgr = GetManager()->GetProfileManager(); const char* profile_name = GetProfileName(); if (profile_name && profile_name[0]) pCachedProfile = *mgr->GetProfile(this, profile_name); if (pCachedProfile == NULL) pCachedProfile = *mgr->GetDefaultProfile(this); } return pCachedProfile.GetPtr(); } const char* HMDDevice::GetProfileName() { if (ProfileName.IsEmpty()) { // If the profile name has not been initialized then // retrieve the stored default user for this specific device ProfileManager* mgr = GetManager()->GetProfileManager(); const char* name = mgr->GetDefaultUser(this); ProfileName = name; } return ProfileName.ToCStr(); } bool HMDDevice::SetProfileName(const char* name) { if (ProfileName == name) return true; // already set // Flush the old profile pCachedProfile.Clear(); if (!name) { ProfileName.Clear(); return false; } // Set the name and attempt to cache the profile ProfileName = name; if (GetProfile()) { return true; } else { ProfileName.Clear(); return false; } } OVR::SensorDevice* HMDDevice::GetSensor() { // Just return first sensor found since we have no way to match it yet. // Create DK2 sensor if it exists otherwise create first DK1 sensor. SensorDevice* sensor = NULL; DeviceEnumerator enumerator = GetManager()->EnumerateDevices(); while(enumerator.GetType() != Device_None) { SensorInfo info; enumerator.GetDeviceInfo(&info); if (info.ProductId == Device_Tracker2_ProductId) { sensor = enumerator.CreateDevice(); break; } enumerator.Next(); } if (sensor == NULL) { sensor = GetManager()->EnumerateDevices().CreateDevice(); } if (sensor) { sensor->SetCoordinateFrame(SensorDevice::Coord_HMD); } return sensor; } \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Device.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Device.h new file mode 100644 index 0000000..0bc8ae9 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Device.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Device.h Content : Definition of HMD-related Device interfaces Created : September 21, 2012 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Device_h #define OVR_Device_h #include "OVR_DeviceConstants.h" #include "OVR_DeviceHandle.h" #include "OVR_DeviceMessages.h" #include "OVR_HIDDeviceBase.h" #include "Kernel/OVR_Atomic.h" #include "Kernel/OVR_RefCount.h" #include "Kernel/OVR_String.h" namespace OVR { // Declared externally class Profile; class ProfileManager; // << Should be renamed for consistency // Forward declarations class SensorDevice; class DeviceCommon; class DeviceManager; // MessageHandler is a base class from which users derive to receive messages, // its OnMessage handler will be called for messages once it is installed on // a device. Same message handler can be installed on multiple devices. class MessageHandler { friend class MessageHandlerImpl; public: MessageHandler(); virtual ~MessageHandler(); // Returns 'true' if handler is currently installed on any devices. bool IsHandlerInstalled() const; // Should be called from derived class destructor to avoid handler // being called after it exits. void RemoveHandlerFromDevices(); // Returns a pointer to the internal lock object that is locked by a // background thread while OnMessage() is called. // This lock guaranteed to survive until ~MessageHandler. Lock* GetHandlerLock() const; virtual void OnMessage(const Message&) { } // Determines if handler supports a specific message type. Can // be used to filter out entire message groups. The result // returned by this function shouldn't change after handler creation. virtual bool SupportsMessageType(MessageType) const { return true; } private: UPInt Internal[8]; }; //------------------------------------------------------------------------------------- // ***** DeviceBase // DeviceBase is the base class for all OVR Devices. It provides the following basic // functionality: // - Reports device type, manager, and associated parent (if any). // - Supports installable message handlers, which are notified of device events. // - Device objects are created through DeviceHandle::CreateDevice or more commonly // through DeviceEnumerator<>::CreateDevice. // - Created devices are reference counted, starting with RefCount of 1. // - Device is resources are cleaned up when it is Released, although its handles // may survive longer if referenced. class DeviceBase : public NewOverrideBase { friend class DeviceHandle; friend class DeviceManagerImpl; public: // Enumerating DeviceBase enumerates all devices. enum { EnumDeviceType = Device_All }; virtual ~DeviceBase() { } virtual void AddRef(); virtual void Release(); virtual DeviceBase* GetParent() const; virtual DeviceManager* GetManager() const; virtual void AddMessageHandler(MessageHandler* handler); virtual DeviceType GetType() const; virtual bool GetDeviceInfo(DeviceInfo* info) const; // Returns true if device is connected and usable virtual bool IsConnected(); // returns the MessageHandler's lock Lock* GetHandlerLock() const; protected: // Internal virtual DeviceCommon* getDeviceCommon() const = 0; }; //------------------------------------------------------------------------------------- // ***** DeviceInfo // DeviceInfo describes a device and its capabilities, obtained by calling // GetDeviceInfo. This base class only contains device-independent functionality; // users will normally use a derived HMDInfo or SensorInfo classes for more // extensive device info. class DeviceInfo { public: DeviceInfo() : InfoClassType(Device_None), Type(Device_None), Version(0) {} // Type of device for which DeviceInfo is intended. // This will be set to Device_HMD for HMDInfo structure, note that this may be // different form the actual device type since (Device_None) is valid. const DeviceType InfoClassType; // Type of device this describes. This must be the same as InfoClassType when // InfoClassType != Device_None. DeviceType Type; // Name string describing the product: "Oculus Rift DK1", etc. String ProductName; String Manufacturer; unsigned Version; protected: DeviceInfo(DeviceType type) : InfoClassType(type), Type(type), Version(0) {} void operator = (const DeviceInfo&) { OVR_ASSERT(0); } // Assignment not allowed. }; //------------------------------------------------------------------------------------- // DeviceEnumerationArgs provides device enumeration argumenrs for DeviceManager::EnumerateDevicesEx. class DeviceEnumerationArgs { public: DeviceEnumerationArgs(DeviceType enumType, bool availableOnly) : EnumType(enumType), AvailableOnly(availableOnly) { } // Helper; returns true if args match our enumeration criteria. bool MatchRule(DeviceType type, bool available) const { return ((EnumType == type) || (EnumType == Device_All)) && (available || !AvailableOnly); } protected: DeviceType EnumType; bool AvailableOnly; }; // DeviceEnumerator<> is used to enumerate and create devices of specified class, // it is returned by calling MeviceManager::EnumerateDevices. Initially, the enumerator will // refer to the first device of specified type. Additional devices can be accessed by // calling Next(). template class DeviceEnumerator : public DeviceHandle { friend class DeviceManager; friend class DeviceManagerImpl; public: DeviceEnumerator() : DeviceHandle(), EnumArgs(Device_None, true) { } // Next advances enumeration to the next device that first criteria. // Returns false if no more devices exist that match enumeration criteria. bool Next() { return enumerateNext(EnumArgs); } // Creates an instance of the device referenced by enumerator; returns null // if enumerator does not refer to a valid device or device is unavailable. // If device was already created, the same object with incremented ref-count is returned. T* CreateDevice() { return static_cast(DeviceHandle::CreateDevice()); } protected: DeviceEnumerator(const DeviceHandle &dev, const DeviceEnumerationArgs& args) : DeviceHandle(dev), EnumArgs(args) { } DeviceEnumerationArgs EnumArgs; }; //------------------------------------------------------------------------------------- // ***** DeviceManager // DeviceManager maintains and provides access to devices supported by OVR, such as // HMDs and sensors. A single instance of DeviceManager is normally created at // program startup, allowing devices to be enumerated and created. DeviceManager is // reference counted and is AddRefed by its created child devices, causing it to // always be the last object that is released. // // Install MessageHandler on DeviceManager to detect when devices are inserted or removed. // // The following code will create the manager and its first available HMDDevice, // and then release it when not needed: // // DeviceManager* manager = DeviceManager::Create(); // HMDDevice* hmd = manager->EnumerateDevices().CreateDevice(); // // if (hmd) hmd->Release(); // if (manager) manager->Release(); class DeviceManager : public DeviceBase { public: DeviceManager() { } // DeviceBase implementation. virtual DeviceType GetType() const { return Device_Manager; } virtual DeviceManager* GetManager() const { return const_cast(this); } // Every DeviceManager has an associated profile manager, which us used to store // user settings that may affect device behavior. virtual ProfileManager* GetProfileManager() const = 0; // EnumerateDevices enumerates all of the available devices of the specified class, // returning an enumerator that references the first device. An empty enumerator is // returned if no devices are available. The following APIs are exposed through // DeviceEnumerator: // DeviceEnumerator::GetType() - Check device type. Returns Device_None // if no device was found/pointed to. // DeviceEnumerator::GetDeviceInfo() - Get more information on device. // DeviceEnumerator::CreateDevice() - Create an instance of device. // DeviceEnumerator::Next() - Move onto next device. template DeviceEnumerator EnumerateDevices(bool availableOnly = true) { // TBD: A cleaner (but less efficient) alternative is though enumeratorFromHandle. DeviceEnumerator<> e = EnumerateDevicesEx(DeviceEnumerationArgs((DeviceType)D::EnumDeviceType, availableOnly)); return *reinterpret_cast*>(&e); } // EnumerateDevicesEx provides internal implementation for device enumeration, enumerating // devices based on dynamically specified DeviceType in DeviceEnumerationArgs. // End users should call DeumerateDevices<>() instead. virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args) = 0; // Creates a new DeviceManager. Only one instance of DeviceManager should be created at a time. static DeviceManager* Create(); // Static constant for this device type, used in template cast type checks. enum { EnumDeviceType = Device_Manager }; // Adds a device (DeviceCreateDesc*) into Devices. Returns NULL, // if unsuccessful or device is already in the list. virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc) = 0; protected: DeviceEnumerator<> enumeratorFromHandle(const DeviceHandle& h, const DeviceEnumerationArgs& args) { return DeviceEnumerator<>(h, args); } DeviceManager* getThis() { return this; } }; //------------------------------------------------------------------------------------- // ***** HMDInfo // This structure describes various aspects of the HMD allowing us to configure rendering. // // Currently included data: // - Physical screen dimensions, resolution, and eye distances. // (some of these will be configurable with a tool in the future). // These arguments allow us to properly setup projection across HMDs. // - DisplayDeviceName for identifying HMD screen; system-specific interpretation. // // TBD: // - Power on/ off? // - Sensor rates and capabilities // - Distortion radius/variables // - Screen update frequency // - Distortion needed flag // - Update modes: // Set update mode: Stereo (both sides together), mono (same in both eyes), // Alternating, Alternating scan-lines. class HMDInfo : public DeviceInfo { public: // Characteristics of the HMD screen and enclosure HmdTypeEnum HmdType; Size ResolutionInPixels; Size ScreenSizeInMeters; float ScreenGapSizeInMeters; float CenterFromTopInMeters; float LensSeparationInMeters; // Timing & shutter data. All values in seconds. struct ShutterInfo { HmdShutterTypeEnum Type; float VsyncToNextVsync; // 1/framerate float VsyncToFirstScanline; // for global shutter, vsync->shutter open. float FirstScanlineToLastScanline; // for global shutter, will be zero. float PixelSettleTime; // estimated. float PixelPersistence; // Full persistence = 1/framerate. } Shutter; // Desktop coordinate position of the screen (can be negative; may not be present on all platforms) int DesktopX; int DesktopY; // Windows: // "\\\\.\\DISPLAY3", etc. Can be used in EnumDisplaySettings/CreateDC. char DisplayDeviceName[32]; // MacOS: int DisplayId; // Constructor initializes all values to 0s. // To create a "virtualized" HMDInfo, use CreateDebugHMDInfo instead. HMDInfo() : DeviceInfo(Device_HMD), HmdType(HmdType_None), ResolutionInPixels(0), ScreenSizeInMeters(0.0f), ScreenGapSizeInMeters(0.0f), CenterFromTopInMeters(0), LensSeparationInMeters(0), DisplayId(0) { DesktopX = 0; DesktopY = 0; DisplayDeviceName[0] = 0; Shutter.Type = HmdShutter_LAST; Shutter.VsyncToNextVsync = 0.0f; Shutter.VsyncToFirstScanline = 0.0f; Shutter.FirstScanlineToLastScanline = 0.0f; Shutter.PixelSettleTime = 0.0f; Shutter.PixelPersistence = 0.0f; } // Operator = copies local fields only (base class must be correct already) void operator = (const HMDInfo& src) { HmdType = src.HmdType; ResolutionInPixels = src.ResolutionInPixels; ScreenSizeInMeters = src.ScreenSizeInMeters; ScreenGapSizeInMeters = src.ScreenGapSizeInMeters; CenterFromTopInMeters = src.CenterFromTopInMeters; LensSeparationInMeters = src.LensSeparationInMeters; DesktopX = src.DesktopX; DesktopY = src.DesktopY; Shutter = src.Shutter; memcpy(DisplayDeviceName, src.DisplayDeviceName, sizeof(DisplayDeviceName)); DisplayId = src.DisplayId; } bool IsSameDisplay(const HMDInfo& o) const { return DisplayId == o.DisplayId && String::CompareNoCase(DisplayDeviceName, o.DisplayDeviceName) == 0; } }; // 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 // first creating a Sensor object and then. // TBD: // - Configure Sensor // - APIs to set On-Screen message, other states? class HMDDevice : public DeviceBase { public: HMDDevice() { } // Static constant for this device type, used in template cast type checks. enum { EnumDeviceType = Device_HMD }; virtual DeviceType GetType() const { return Device_HMD; } // Creates a sensor associated with this HMD. virtual SensorDevice* GetSensor() = 0; // Requests the currently used profile. This profile affects the // settings reported by HMDInfo. virtual Profile* GetProfile() = 0; // Obtains the currently used profile name. This is initialized to the default // profile name, if any; it can then be changed per-device by SetProfileName. virtual const char* GetProfileName() = 0; // Sets the profile user name, changing the data returned by GetProfileInfo. virtual bool SetProfileName(const char* name) = 0; // Disconnects from real HMD device. This HMDDevice remains as 'fake' HMD. // SensorDevice ptr is used to restore the 'fake' HMD (can be NULL). HMDDevice* Disconnect(SensorDevice*); // Returns 'true' if HMD device is a 'fake' HMD (was created this way or // 'Disconnect' method was called). bool IsDisconnected() const; }; //------------------------------------------------------------------------------------- // ***** SensorRange & SensorInfo // SensorRange specifies maximum value ranges that SensorDevice hardware is configured // to detect. Although this range doesn't affect the scale of MessageBodyFrame values, // physical motions whose positive or negative magnitude is outside the specified range // may get clamped or misreported. Setting lower values may result in higher precision // tracking. struct SensorRange { SensorRange(float maxAcceleration = 0.0f, float maxRotationRate = 0.0f, float maxMagneticField = 0.0f) : MaxAcceleration(maxAcceleration), MaxRotationRate(maxRotationRate), MaxMagneticField(maxMagneticField) { } // Maximum detected acceleration in m/s^2. Up to 8*G equivalent support guaranteed, // where G is ~9.81 m/s^2. // Oculus DK1 HW has thresholds near: 2, 4 (default), 8, 16 G. float MaxAcceleration; // Maximum detected angular velocity in rad/s. Up to 8*Pi support guaranteed. // Oculus DK1 HW thresholds near: 1, 2, 4, 8 Pi (default). float MaxRotationRate; // Maximum detectable Magnetic field strength in Gauss. Up to 2.5 Gauss support guaranteed. // Oculus DK1 HW thresholds near: 0.88, 1.3, 1.9, 2.5 gauss. float MaxMagneticField; }; // SensorInfo describes capabilities of the sensor device. class SensorInfo : public DeviceInfo { public: SensorInfo() : DeviceInfo(Device_Sensor), VendorId(0), ProductId(0) { } // HID Vendor and ProductId of the device. UInt16 VendorId; UInt16 ProductId; // MaxRanges report maximum sensor range values supported by HW. SensorRange MaxRanges; // Sensor (and display) serial number. String SerialNumber; private: void operator = (const SensorInfo&) { OVR_ASSERT(0); } // Assignment not allowed. }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Serial Number feature report. (DK1) struct SerialReport { static const int SERIAL_NUMBER_SIZE = 12; // Serial Number size = 12 bytes. (Refer 'Tracker Firmware Specification Section 4.9, Pg 18) SerialReport() : CommandId(0) { memset(SerialNumberValue, 0, sizeof(SerialNumberValue)); } SerialReport(UInt16 commandId, UByte SNo[SERIAL_NUMBER_SIZE]) : CommandId(commandId) { for (int i=0; i < SERIAL_NUMBER_SIZE; i++) { SerialNumberValue[i] = SNo[i]; } } UInt16 CommandId; UByte SerialNumberValue[SERIAL_NUMBER_SIZE]; // See 'Tracker Firmware Specification' document for // a description of Serial Report. }; //////////////////////////////////////////////////////////////////////////////////////////////// //Added Serial Report Implementation. struct SerialImpl { enum { PacketSize = 15 }; UByte Buffer[PacketSize]; SerialReport Settings; SerialImpl() { memset(Buffer, 0, sizeof(Buffer)); Buffer[0] = 10; } SerialImpl(const SerialReport& settings) :Settings(settings) { Pack(); } void Pack() { Buffer[0] = 10; Alg::EncodeUInt16(Buffer+1, Settings.CommandId); for (int i = 0; i < Settings.SERIAL_NUMBER_SIZE; ++i) Buffer[3 + i] = Settings.SerialNumberValue[i]; } void Unpack() { Settings.CommandId = Alg::DecodeUInt16(Buffer+1); for (int i = 0; i < Settings.SERIAL_NUMBER_SIZE; ++i) Settings.SerialNumberValue[i] = Buffer[3 + i]; } }; // Tracking settings (DK2). struct TrackingReport { TrackingReport() : CommandId(0), Pattern(0), Enable(0), Autoincrement(0), UseCarrier(0), SyncInput(0), VsyncLock(0), CustomPattern(0), ExposureLength(0), FrameInterval(0), VsyncOffset(0), DutyCycle(0) {} TrackingReport( UInt16 commandId, UByte pattern, bool enable, bool autoincrement, bool useCarrier, bool syncInput, bool vsyncLock, bool customPattern, UInt16 exposureLength, UInt16 frameInterval, UInt16 vsyncOffset, UByte dutyCycle) : CommandId(commandId), Pattern(pattern), Enable(enable), Autoincrement(autoincrement), UseCarrier(useCarrier), SyncInput(syncInput), VsyncLock(vsyncLock), CustomPattern(customPattern), ExposureLength(exposureLength), FrameInterval(frameInterval), VsyncOffset(vsyncOffset), DutyCycle(dutyCycle) { } UInt16 CommandId; UByte Pattern; // Tracking LED pattern index. bool Enable; // Enables the tracking LED exposure and updating. bool Autoincrement; // Autoincrement pattern after each exposure. bool UseCarrier; // Modulate tracking LEDs at 85kHz. bool SyncInput; // Trigger LED exposure from wired sync signal. bool VsyncLock; // Trigger LED exposure from panel Vsync. bool CustomPattern; // Use custom LED sequence. UInt16 ExposureLength; // Tracking LED illumination (and exposure) length in microseconds. UInt16 FrameInterval; // LED exposure interval in microseconds when in // 'internal timer' mode (when SyncInput = VsyncLock = false). UInt16 VsyncOffset; // Exposure offset in microseconds from vsync when in // 'vsync lock' mode (when VsyncLock = true). UByte DutyCycle; // Duty cycle of 85kHz modulation when in 'use carrier' mode // (when UseCarrier = true). 128 = 50% duty cycle. }; // Display settings (DK2). struct DisplayReport { enum ShutterTypeEnum { // These are not yet defined. ShutterType_Default = 0, }; enum CurrentLimitEnum { // These are not yet defined. CurrentLimit_Default = 0, }; DisplayReport() : CommandId(0), Brightness(0), ShutterType(ShutterType_Default), CurrentLimit(CurrentLimit_Default), UseRolling(0), ReverseRolling(0), HighBrightness(0), SelfRefresh(0), ReadPixel(0), DirectPentile(0), Persistence(0), LightingOffset(0), PixelSettle(0), TotalRows(0) {} DisplayReport( UInt16 commandId, UByte brightness, ShutterTypeEnum shutterType, CurrentLimitEnum currentLimit, bool useRolling, bool reverseRolling, bool highBrightness, bool selfRefresh, bool readPixel, bool directPentile, UInt16 persistence, UInt16 lightingOffset, UInt16 pixelSettle, UInt16 totalRows) : CommandId(commandId), Brightness(brightness), ShutterType(shutterType), CurrentLimit(currentLimit), UseRolling(useRolling), ReverseRolling(reverseRolling), HighBrightness(highBrightness), SelfRefresh(selfRefresh), ReadPixel(readPixel), DirectPentile(directPentile), Persistence(persistence), LightingOffset(lightingOffset), PixelSettle(pixelSettle), TotalRows(totalRows) { } UInt16 CommandId; UByte Brightness; // See 'DK2 Firmware Specification' document for a description of ShutterTypeEnum ShutterType; // display settings. CurrentLimitEnum CurrentLimit; bool UseRolling; bool ReverseRolling; bool HighBrightness; bool SelfRefresh; bool ReadPixel; bool DirectPentile; UInt16 Persistence; UInt16 LightingOffset; UInt16 PixelSettle; UInt16 TotalRows; }; // MagCalibration matrix (DK2). struct MagCalibrationReport { MagCalibrationReport() : CommandId(0), Version(0), Calibration() {} MagCalibrationReport( UInt16 commandId, UByte version, const Matrix4f& calibration) : CommandId(commandId), Version(version), Calibration(calibration) { } UInt16 CommandId; UByte Version; // Version of the calibration procedure used to generate the calibration matrix. Matrix4f Calibration; // Calibration matrix. Note only the first three rows are used by the feature report. }; // PositionCalibration values (DK2). // - Sensor interface versions before 5 do not support Normal and Rotation. struct PositionCalibrationReport { enum PositionTypeEnum { PositionType_LED = 0, PositionType_IMU = 1 }; PositionCalibrationReport() : CommandId(0), Version(0), Position(0), Normal(0), Angle(0), PositionIndex(0), NumPositions(0), PositionType(PositionType_LED) {} PositionCalibrationReport(UInt16 commandId, UByte version, const Vector3d& position, const Vector3d& normal, double rotation, UInt16 positionIndex, UInt16 numPositions, PositionTypeEnum positionType) : CommandId(commandId), Version(version), Position(position), Normal(normal), Angle(rotation), PositionIndex(positionIndex), NumPositions(numPositions), PositionType(positionType) { } UInt16 CommandId; UByte Version; // The version of the calibration procedure used to generate the stored positions. Vector3d Position; // Position of the LED or inertial tracker in meters. This is relative to the // center of the emitter plane of the display at nominal focus. Vector3d Normal; // Normal of the LED or inertial tracker. This is a signed integer in // meters. The normal is relative to the position. double Angle; // The rotation about the normal. This is in radians. UInt16 PositionIndex; // The current position being read or written to. Autoincrements on reads, gets set // to the written value on writes. UInt16 NumPositions; // The read-only number of items with positions stored. The last position is that of // the inertial tracker, all others are LED positions. PositionTypeEnum PositionType; // The type of the item which has its position reported in the current report }; // CustomPattern values (DK2). struct CustomPatternReport { CustomPatternReport() : CommandId(0), SequenceLength(0), Sequence(0), LEDIndex(0), NumLEDs(0) {} CustomPatternReport(UInt16 commandId, UByte sequenceLength, UInt32 sequence, UInt16 ledIndex, UInt16 numLEDs) : CommandId(commandId), SequenceLength(sequenceLength), Sequence(sequence), LEDIndex(ledIndex), NumLEDs(numLEDs) { } UInt16 CommandId; UByte SequenceLength; // See 'DK2 Firmware Specification' document for a description of UInt32 Sequence; // LED custom patterns. UInt16 LEDIndex; UInt16 NumLEDs; }; // KeepAliveMux settings (DK2). struct KeepAliveMuxReport { KeepAliveMuxReport() : CommandId(0), INReport(0), Interval(0) {} KeepAliveMuxReport( UInt16 commandId, UByte inReport, UInt16 interval) : CommandId(commandId), INReport(inReport), Interval(interval) { } UInt16 CommandId; UByte INReport; // Requested IN report type (1 = DK1, 11 = DK2). UInt16 Interval; // Keep alive period in milliseconds. }; // Manufacturing test result (DK2). struct ManufacturingReport { ManufacturingReport() : CommandId(0), NumStages(0), Stage(0), StageVersion(0), StageLocation(0), StageTime(0), Result(0) {} ManufacturingReport( UInt16 commandId, UByte numStages, UByte stage, UByte version, UInt16 stageLocation, UInt32 stageTime, UInt32 result) : CommandId(commandId), NumStages(numStages), Stage(stage), StageVersion(version), StageLocation(stageLocation), StageTime(stageTime), Result(result) { } UInt16 CommandId; UByte NumStages; // See 'DK2 Firmware Specification' document for a description of UByte Stage; // manufacturing test results. UByte StageVersion; UInt16 StageLocation; UInt32 StageTime; UInt32 Result; }; // UUID (DK2). struct UUIDReport { static const int UUID_SIZE = 20; UUIDReport() : CommandId(0) { memset(UUIDValue, 0, sizeof(UUIDValue)); } UUIDReport( UInt16 commandId, UByte uuid[UUID_SIZE]) : CommandId(commandId) { for (int i=0; i*) { return false; } virtual bool SetCustomPatternReport(const CustomPatternReport&) { return false; } virtual bool GetCustomPatternReport(CustomPatternReport*) { return false; } virtual bool SetKeepAliveMuxReport(const KeepAliveMuxReport&) { return false; } virtual bool GetKeepAliveMuxReport(KeepAliveMuxReport*) { return false; } virtual bool SetManufacturingReport(const ManufacturingReport&) { return false; } virtual bool GetManufacturingReport(ManufacturingReport*) { return false; } virtual bool SetUUIDReport(const UUIDReport&) { return false; } virtual bool GetUUIDReport(UUIDReport*) { return false; } virtual bool SetTemperatureReport(const TemperatureReport&) { return false; } virtual bool GetAllTemperatureReports(Array >*) { return false; } virtual bool GetGyroOffsetReport(GyroOffsetReport*) { return false; } virtual bool SetLensDistortionReport(const LensDistortionReport&) { return false; } virtual bool GetLensDistortionReport(LensDistortionReport*) { return false; } }; //------------------------------------------------------------------------------------- // ***** LatencyTestConfiguration // LatencyTestConfiguration specifies configuration information for the Oculus Latency Tester device. struct LatencyTestConfiguration { LatencyTestConfiguration(const Color& threshold, bool sendSamples = false) : Threshold(threshold), SendSamples(sendSamples) { } // The color threshold for triggering a detected display change. Color Threshold; // Flag specifying whether we wish to receive a stream of color values from the sensor. bool SendSamples; }; //------------------------------------------------------------------------------------- // ***** LatencyTestDisplay // LatencyTestDisplay sets the mode and contents of the Latency Tester LED display. // See the 'Latency Tester Specification' document for more details. struct LatencyTestDisplay { LatencyTestDisplay(UByte mode, UInt32 value) : Mode(mode), Value(value) { } UByte Mode; // The display mode that we wish to select. UInt32 Value; // The value to display. }; //------------------------------------------------------------------------------------- // ***** LatencyTestDevice // LatencyTestDevice provides an interface to the Oculus Latency Tester which is used to test 'motion to photon' latency. class LatencyTestDevice : public HIDDeviceBase, public DeviceBase { public: LatencyTestDevice() { } // Static constant for this device type, used in template cast type checks. enum { EnumDeviceType = Device_LatencyTester }; virtual DeviceType GetType() const { return Device_LatencyTester; } // Specifies configuration information including the threshold for triggering a detected color change, // and a flag to enable a stream of sensor values (typically used for debugging). virtual bool SetConfiguration(const LatencyTestConfiguration& configuration, bool waitFlag = false) = 0; // Get configuration information from device. virtual bool GetConfiguration(LatencyTestConfiguration* configuration) = 0; // Used to calibrate the latency tester at the start of a test. Display the specified color on the screen // beneath the latency tester and then call this method. Calibration information is lost // when power is removed from the device. virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false) = 0; // Triggers the start of a measurement. This starts the millisecond timer on the device and // causes it to respond with the 'MessageLatencyTestStarted' message. virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false) = 0; // Used to set the value displayed on the LED display panel. virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false) = 0; virtual DeviceBase* GetDevice() { return this; } }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceConstants.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceConstants.h new file mode 100644 index 0000000..6cf4c2d --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceConstants.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_DeviceConstants.h Content : Device constants Created : February 5, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_DeviceConstants_h #define OVR_DeviceConstants_h namespace OVR { //------------------------------------------------------------------------------------- // Different device types supported by OVR; this type is reported by DeviceBase::GetType. // enum DeviceType { Device_None = 0, Device_Manager = 1, Device_HMD = 2, Device_Sensor = 3, Device_LatencyTester = 4, Device_BootLoader = 5, Device_Camera = 6, Device_Display = 7, Device_All = 0xFF // Set for enumeration only, to enumerate all device types. }; //------------------------------------------------------------------------------------- // Different lens distortion types supported by devices. // enum DistortionEqnType { Distortion_No_Override = -1, // These two are leagcy and deprecated. Distortion_Poly4 = 0, // scale = (K0 + K1*r^2 + K2*r^4 + K3*r^6) Distortion_RecipPoly4 = 1, // scale = 1/(K0 + K1*r^2 + K2*r^4 + K3*r^6) // CatmullRom10 is the preferred distortion format. Distortion_CatmullRom10 = 2, // scale = Catmull-Rom spline through points (1.0, K[1]...K[9]) Distortion_LAST // For ease of enumeration. }; //------------------------------------------------------------------------------------- // HMD types. // enum HmdTypeEnum { HmdType_None, HmdType_DKProto, // First duct-tape model, never sold. HmdType_DK1, // DevKit1 - on sale to developers. HmdType_DKHDProto, // DKHD - shown at various shows, never sold. HmdType_DKHD2Proto, // DKHD2, 5.85-inch panel, never sold. HmdType_DKHDProto566Mi, // DKHD, 5.66-inch panel, never sold. HmdType_CrystalCoveProto, // Crystal Cove, 5.66-inch panel, shown at shows but never sold. HmdType_DK2, // Reminder - this header file is public - codenames only! HmdType_Unknown, // Used for unnamed HW lab experiments. HmdType_LAST }; //------------------------------------------------------------------------------------- // HMD shutter types. // enum HmdShutterTypeEnum { HmdShutter_Global, HmdShutter_RollingTopToBottom, HmdShutter_RollingLeftToRight, HmdShutter_RollingRightToLeft, // TODO: // color-sequential e.g. LCOS? // alternate eyes? // alternate columns? // outside-in? HmdShutter_LAST }; //------------------------------------------------------------------------------------- // For headsets that use eye cups // enum EyeCupType { // Public lenses EyeCup_DK1A = 0, EyeCup_DK1B = 1, EyeCup_DK1C = 2, EyeCup_DK2A = 3, // Internal R&D codenames. // Reminder - this header file is public - codenames only! EyeCup_DKHD2A, EyeCup_OrangeA, EyeCup_RedA, EyeCup_PinkA, EyeCup_BlueA, EyeCup_Delilah1A, EyeCup_Delilah2A, EyeCup_JamesA, EyeCup_SunMandalaA, EyeCup_LAST }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceHandle.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceHandle.cpp new file mode 100644 index 0000000..1931f6b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceHandle.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_DeviceHandle.cpp Content : Implementation of device handle class Created : February 5, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_DeviceHandle.h" #include "OVR_DeviceImpl.h" namespace OVR { //------------------------------------------------------------------------------------- // ***** DeviceHandle DeviceHandle::DeviceHandle(DeviceCreateDesc* impl) : pImpl(impl) { if (pImpl) pImpl->AddRef(); } DeviceHandle::DeviceHandle(const DeviceHandle& src) : pImpl(src.pImpl) { if (pImpl) pImpl->AddRef(); } DeviceHandle::~DeviceHandle() { if (pImpl) pImpl->Release(); } void DeviceHandle::operator = (const DeviceHandle& src) { if (src.pImpl) src.pImpl->AddRef(); if (pImpl) pImpl->Release(); pImpl = src.pImpl; } DeviceBase* DeviceHandle::GetDevice_AddRef() const { if (pImpl && pImpl->pDevice) { pImpl->pDevice->AddRef(); return pImpl->pDevice; } return NULL; } // Returns true, if the handle contains the same device ptr // as specified in the parameter. bool DeviceHandle::IsDevice(DeviceBase* pdev) const { return (pdev && pImpl && pImpl->pDevice) ? pImpl->pDevice == pdev : false; } DeviceType DeviceHandle::GetType() const { return pImpl ? pImpl->Type : Device_None; } bool DeviceHandle::GetDeviceInfo(DeviceInfo* info) const { return pImpl ? pImpl->GetDeviceInfo(info) : false; } bool DeviceHandle::IsAvailable() const { // This isn't "atomically safe", but the function only returns the // recent state that may change. return pImpl ? (pImpl->Enumerated && pImpl->pLock->pManager) : false; } bool DeviceHandle::IsCreated() const { return pImpl ? (pImpl->pDevice != 0) : false; } DeviceBase* DeviceHandle::CreateDevice() { if (!pImpl) return 0; DeviceBase* device = 0; Ptr manager= 0; // Since both manager and device pointers can only be destroyed during a lock, // hold it while checking for availability. // AddRef to manager so that it doesn't get released on us. { Lock::Locker deviceLockScope(pImpl->GetLock()); if (pImpl->pDevice) { pImpl->pDevice->AddRef(); return pImpl->pDevice; } manager = pImpl->GetManagerImpl(); } if (manager) { if (manager->GetThreadId() != OVR::GetCurrentThreadId()) { // Queue up a CreateDevice request. This fills in '&device' with AddRefed value, // or keep it at null. manager->GetThreadQueue()->PushCallAndWaitResult( manager.GetPtr(), &DeviceManagerImpl::CreateDevice_MgrThread, &device, pImpl, (DeviceBase*)0); } else device = manager->CreateDevice_MgrThread(pImpl, (DeviceBase*)0); } return device; } void DeviceHandle::Clear() { if (pImpl) { pImpl->Release(); pImpl = 0; } } bool DeviceHandle::enumerateNext(const DeviceEnumerationArgs& args) { if (GetType() == Device_None) return false; Ptr managerKeepAlive; Lock::Locker lockScope(pImpl->GetLock()); DeviceCreateDesc* next = pImpl; // If manager was destroyed, we get removed from the list. if (!pImpl->pNext) return false; managerKeepAlive = next->GetManagerImpl(); OVR_ASSERT(managerKeepAlive); do { next = next->pNext; if (managerKeepAlive->Devices.IsNull(next)) { pImpl->Release(); pImpl = 0; return false; } } while(!args.MatchRule(next->Type, next->Enumerated)); next->AddRef(); pImpl->Release(); pImpl = next; return true; } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceHandle.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceHandle.h new file mode 100644 index 0000000..0171709 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceHandle.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_DeviceHandle.h Content : Handle to a device that was enumerated Created : February 5, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_DeviceHandle_h #define OVR_DeviceHandle_h #include "OVR_DeviceConstants.h" namespace OVR { class DeviceBase; class DeviceInfo; // Internal class DeviceCreateDesc; class DeviceEnumerationArgs; //------------------------------------------------------------------------------------- // ***** DeviceHandle // DeviceHandle references a specific device that was enumerated; it can be assigned // directly from DeviceEnumerator. // // Devices represented by DeviceHandle are not necessarily created or available. // A device may become unavailable if, for example, it its unplugged. If the device // is available, it can be created by calling CreateDevice. // class DeviceHandle { friend class DeviceManager; friend class DeviceManagerImpl; template friend class HIDDeviceImpl; public: DeviceHandle() : pImpl(0) { } DeviceHandle(const DeviceHandle& src); ~DeviceHandle(); void operator = (const DeviceHandle& src); bool operator == (const DeviceHandle& other) const { return pImpl == other.pImpl; } bool operator != (const DeviceHandle& other) const { return pImpl != other.pImpl; } // operator bool() returns true if Handle/Enumerator points to a valid device. operator bool () const { return GetType() != Device_None; } // Returns existing device, or NULL if !IsCreated. The returned ptr is // addref-ed. DeviceBase* GetDevice_AddRef() const; DeviceType GetType() const; bool GetDeviceInfo(DeviceInfo* info) const; bool IsAvailable() const; bool IsCreated() const; // Returns true, if the handle contains the same device ptr // as specified in the parameter. bool IsDevice(DeviceBase*) const; // Creates a device, or returns AddRefed pointer if one is already created. // New devices start out with RefCount of 1. DeviceBase* CreateDevice(); // Creates a device, or returns AddRefed pointer if one is already created. // New devices start out with RefCount of 1. DeviceT is used to cast the // DeviceBase* to a concreete type. template DeviceT* CreateDeviceTyped() const { return static_cast(DeviceHandle(*this).CreateDevice()); } // Resets the device handle to uninitialized state. void Clear(); protected: explicit DeviceHandle(DeviceCreateDesc* impl); bool enumerateNext(const DeviceEnumerationArgs& args); DeviceCreateDesc* pImpl; }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceImpl.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceImpl.cpp new file mode 100644 index 0000000..e1b07f7 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceImpl.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_DeviceImpl.h Content : Partial back-end independent implementation of Device interfaces Created : October 10, 2012 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_DeviceImpl.h" #include "Kernel/OVR_Atomic.h" #include "Kernel/OVR_Log.h" #include "Kernel/OVR_System.h" #include "OVR_DeviceImpl.h" #include "OVR_SensorImpl.h" #include "OVR_Profile.h" namespace OVR { //------------------------------------------------------------------------------------- // ***** MessageHandler // Threading notes: // The OnMessage() handler and SetMessageHandler are currently synchronized // through a separately stored shared Lock object to avoid calling the handler // from background thread while it's being removed. static SharedLock MessageHandlerSharedLock; class MessageHandlerImpl { public: enum { MaxHandlerRefsCount = 4 }; MessageHandlerImpl() : pLock(MessageHandlerSharedLock.GetLockAddRef()), HandlerRefsCount(0) { } ~MessageHandlerImpl() { MessageHandlerSharedLock.ReleaseLock(pLock); pLock = 0; } static MessageHandlerImpl* FromHandler(MessageHandler* handler) { return (MessageHandlerImpl*)&handler->Internal; } static const MessageHandlerImpl* FromHandler(const MessageHandler* handler) { return (const MessageHandlerImpl*)&handler->Internal; } // This lock is held while calling a handler and when we are applied/ // removed from a device. Lock* pLock; // List of devices we are applied to. int HandlerRefsCount; MessageHandlerRef* pHandlerRefs[MaxHandlerRefsCount]; }; MessageHandlerRef::MessageHandlerRef(DeviceBase* device) : pLock(MessageHandlerSharedLock.GetLockAddRef()), pDevice(device), HandlersCount(0) { } MessageHandlerRef::~MessageHandlerRef() { { Lock::Locker lockScope(pLock); while (HandlersCount > 0) removeHandler(0); } MessageHandlerSharedLock.ReleaseLock(pLock); pLock = 0; } void MessageHandlerRef::Call(const Message& msg) { Lock::Locker lockScope(pLock); for (int i = 0; i < HandlersCount; i++) pHandlers[i]->OnMessage(msg); } void MessageHandlerRef::AddHandler(MessageHandler* handler) { OVR_ASSERT(!handler || MessageHandlerImpl::FromHandler(handler)->pLock == pLock); Lock::Locker lockScope(pLock); AddHandler_NTS(handler); } void MessageHandlerRef::AddHandler_NTS(MessageHandler* handler) { OVR_ASSERT(handler != NULL); OVR_ASSERT(HandlersCount < MaxHandlersCount); for (int i = 0; i < HandlersCount; i++) if (pHandlers[i] == handler) // handler already installed - do nothing return; pHandlers[HandlersCount] = handler; HandlersCount++; MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(handler); OVR_ASSERT(handlerImpl->HandlerRefsCount < MessageHandlerImpl::MaxHandlerRefsCount); handlerImpl->pHandlerRefs[handlerImpl->HandlerRefsCount] = this; handlerImpl->HandlerRefsCount++; // TBD: Call notifier on device? } bool MessageHandlerRef::RemoveHandler(MessageHandler* handler) { Lock::Locker lockScope(pLock); for (int i = 0; i < HandlersCount; i++) { if (pHandlers[i] == handler) return removeHandler(i); } return false; } bool MessageHandlerRef::removeHandler(int idx) { OVR_ASSERT(idx < HandlersCount); MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(pHandlers[idx]); for (int i = 0; i < handlerImpl->HandlerRefsCount; i++) if (handlerImpl->pHandlerRefs[i] == this) { handlerImpl->pHandlerRefs[i] = handlerImpl->pHandlerRefs[handlerImpl->HandlerRefsCount - 1]; handlerImpl->HandlerRefsCount--; pHandlers[idx] = pHandlers[HandlersCount - 1]; HandlersCount--; return true; } // couldn't find a link in the opposite direction, assert in Debug OVR_ASSERT(0); pHandlers[idx] = pHandlers[HandlersCount - 1]; HandlersCount--; return true; } MessageHandler::MessageHandler() { OVR_COMPILER_ASSERT(sizeof(Internal) > sizeof(MessageHandlerImpl)); Construct(Internal); } MessageHandler::~MessageHandler() { MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); { Lock::Locker lockedScope(handlerImpl->pLock); OVR_ASSERT_LOG(handlerImpl->HandlerRefsCount == 0, ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this)); } Destruct(handlerImpl); } bool MessageHandler::IsHandlerInstalled() const { const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); Lock::Locker lockedScope(handlerImpl->pLock); return handlerImpl->HandlerRefsCount > 0; } void MessageHandler::RemoveHandlerFromDevices() { MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); Lock::Locker lockedScope(handlerImpl->pLock); while (handlerImpl->HandlerRefsCount > 0) { MessageHandlerRef* use = handlerImpl->pHandlerRefs[0]; use->RemoveHandler(this); } } Lock* MessageHandler::GetHandlerLock() const { const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); return handlerImpl->pLock; } //------------------------------------------------------------------------------------- // ***** DeviceBase // Delegate relevant implementation to DeviceRectord to avoid re-implementation in // every derived Device. void DeviceBase::AddRef() { getDeviceCommon()->DeviceAddRef(); } void DeviceBase::Release() { getDeviceCommon()->DeviceRelease(); } DeviceBase* DeviceBase::GetParent() const { return getDeviceCommon()->pParent.GetPtr(); } DeviceManager* DeviceBase::GetManager() const { return getDeviceCommon()->pCreateDesc->GetManagerImpl(); } void DeviceBase::AddMessageHandler(MessageHandler* handler) { getDeviceCommon()->HandlerRef.AddHandler(handler); } DeviceType DeviceBase::GetType() const { return getDeviceCommon()->pCreateDesc->Type; } bool DeviceBase::GetDeviceInfo(DeviceInfo* info) const { return getDeviceCommon()->pCreateDesc->GetDeviceInfo(info); //info->Name[0] = 0; //return false; } // Returns true if device is connected and usable bool DeviceBase::IsConnected() { return getDeviceCommon()->ConnectedFlag; } // returns the MessageHandler's lock Lock* DeviceBase::GetHandlerLock() const { return getDeviceCommon()->HandlerRef.GetLock(); } // Derive DeviceManagerCreateDesc to provide abstract function implementation. class DeviceManagerCreateDesc : public DeviceCreateDesc { public: DeviceManagerCreateDesc(DeviceFactory* factory) : DeviceCreateDesc(factory, Device_Manager) { } // We don't need there on Manager since it isn't assigned to DeviceHandle. virtual DeviceCreateDesc* Clone() const { return 0; } virtual MatchResult MatchDevice(const DeviceCreateDesc&, DeviceCreateDesc**) const { return Match_None; } virtual DeviceBase* NewDeviceInstance() { return 0; } virtual bool GetDeviceInfo(DeviceInfo*) const { return false; } }; //------------------------------------------------------------------------------------- // ***** DeviceManagerImpl DeviceManagerImpl::DeviceManagerImpl() : DeviceImpl(CreateManagerDesc(), 0) //,DeviceCreateDescList(pCreateDesc ? pCreateDesc->pLock : 0) { if (pCreateDesc) { pCreateDesc->pLock->pManager = this; } } DeviceManagerImpl::~DeviceManagerImpl() { // Shutdown must've been called. OVR_ASSERT(!pCreateDesc->pDevice); // Remove all factories while(!Factories.IsEmpty()) { DeviceFactory* factory = Factories.GetFirst(); factory->RemovedFromManager(); factory->RemoveNode(); } } DeviceCreateDesc* DeviceManagerImpl::CreateManagerDesc() { DeviceCreateDesc* managerDesc = new DeviceManagerCreateDesc(0); if (managerDesc) { managerDesc->pLock = *new DeviceManagerLock; } return managerDesc; } bool DeviceManagerImpl::Initialize(DeviceBase* parent) { OVR_UNUSED(parent); if (!pCreateDesc || !pCreateDesc->pLock) return false; pProfileManager = *ProfileManager::Create(); return true; } void DeviceManagerImpl::Shutdown() { // Remove all device descriptors from list while the lock is held. // Some descriptors may survive longer due to handles. while(!Devices.IsEmpty()) { DeviceCreateDesc* devDesc = Devices.GetFirst(); OVR_ASSERT(!devDesc->pDevice); // Manager shouldn't be dying while Device exists. devDesc->Enumerated = false; devDesc->RemoveNode(); devDesc->pNext = devDesc->pPrev = 0; if (devDesc->HandleCount == 0) { delete devDesc; } } Devices.Clear(); // These must've been cleared by caller. OVR_ASSERT(pCreateDesc->pDevice == 0); OVR_ASSERT(pCreateDesc->pLock->pManager == 0); pProfileManager.Clear(); } // Callbacks for DeviceCreation/Release DeviceBase* DeviceManagerImpl::CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent) { // Calls to DeviceManagerImpl::CreateDevice are enqueued with wait while holding pManager, // so 'this' must remain valid. OVR_ASSERT(createDesc->pLock->pManager); Lock::Locker devicesLock(GetLock()); // If device already exists, just AddRef to it. if (createDesc->pDevice) { createDesc->pDevice->AddRef(); return createDesc->pDevice; } if (!parent) parent = this; DeviceBase* device = createDesc->NewDeviceInstance(); if (device) { if (device->getDeviceCommon()->Initialize(parent)) { createDesc->pDevice = device; } else { // Don't go through Release() to avoid PushCall behaviour, // as it is not needed here. delete device; device = 0; } } return device; } Void DeviceManagerImpl::ReleaseDevice_MgrThread(DeviceBase* device) { // descKeepAlive will keep ManagerLock object alive as well, // allowing us to exit gracefully. Ptr descKeepAlive; Lock::Locker devicesLock(GetLock()); DeviceCommon* devCommon = device->getDeviceCommon(); while(1) { UInt32 refCount = devCommon->RefCount; if (refCount > 1) { if (devCommon->RefCount.CompareAndSet_NoSync(refCount, refCount-1)) { // We decreented from initial count higher then 1; // nothing else to do. return 0; } } else if (devCommon->RefCount.CompareAndSet_NoSync(1, 0)) { // { 1 -> 0 } decrement succeded. Destroy this device. break; } } // At this point, may be releasing the device manager itself. // This does not matter, however, since shutdown logic is the same // in both cases. DeviceManager::Shutdown with begin shutdown process for // the internal manager thread, which will eventually destroy itself. // TBD: Clean thread shutdown. descKeepAlive = devCommon->pCreateDesc; descKeepAlive->pDevice = 0; devCommon->Shutdown(); delete device; return 0; } Void DeviceManagerImpl::EnumerateAllFactoryDevices() { // 1. Mark matching devices as NOT enumerated. // 2. Call factory to enumerate all HW devices, adding any device that // was not matched. // 3. Remove non-matching devices. Lock::Locker deviceLock(GetLock()); DeviceCreateDesc* devDesc, *nextdevDesc; // 1. for(devDesc = Devices.GetFirst(); !Devices.IsNull(devDesc); devDesc = devDesc->pNext) { //if (devDesc->pFactory == factory) devDesc->Enumerated = false; } // 2. DeviceFactory* factory = Factories.GetFirst(); while(!Factories.IsNull(factory)) { EnumerateFactoryDevices(factory); factory = factory->pNext; } // 3. for(devDesc = Devices.GetFirst(); !Devices.IsNull(devDesc); devDesc = nextdevDesc) { // In case 'devDesc' gets removed. nextdevDesc = devDesc->pNext; // Note, device might be not enumerated since it is opened and // in use! Do NOT notify 'device removed' in this case (!AB) if (!devDesc->Enumerated) { // This deletes the devDesc for HandleCount == 0 due to Release in DeviceHandle. CallOnDeviceRemoved(devDesc); /* if (devDesc->HandleCount == 0) { // Device must be dead if it ever existed, since it AddRefs to us. // ~DeviceCreateDesc removes its node from list. OVR_ASSERT(!devDesc->pDevice); delete devDesc; } */ } } return 0; } Ptr DeviceManagerImpl::AddDevice_NeedsLock( const DeviceCreateDesc& createDesc) { // If found, mark as enumerated and we are done. DeviceCreateDesc* descCandidate = 0; for(DeviceCreateDesc* devDesc = Devices.GetFirst(); !Devices.IsNull(devDesc); devDesc = devDesc->pNext) { DeviceCreateDesc::MatchResult mr = devDesc->MatchDevice(createDesc, &descCandidate); if (mr == DeviceCreateDesc::Match_Found) { devDesc->Enumerated = true; if (!devDesc->pDevice) CallOnDeviceAdded(devDesc); return devDesc; } } // Update candidate (this may involve writing fields to HMDDevice createDesc). if (descCandidate) { bool newDevice = false; if (descCandidate->UpdateMatchedCandidate(createDesc, &newDevice)) { descCandidate->Enumerated = true; if (!descCandidate->pDevice || newDevice) CallOnDeviceAdded(descCandidate); return descCandidate; } } // If not found, add new device. // - This stores a new descriptor with // {pDevice = 0, HandleCount = 1, Enumerated = true} DeviceCreateDesc* desc = createDesc.Clone(); desc->pLock = pCreateDesc->pLock; Devices.PushBack(desc); desc->Enumerated = true; CallOnDeviceAdded(desc); return desc; } Ptr DeviceManagerImpl::FindDevice( const String& path, DeviceType deviceType) { Lock::Locker deviceLock(GetLock()); DeviceCreateDesc* devDesc; for (devDesc = Devices.GetFirst(); !Devices.IsNull(devDesc); devDesc = devDesc->pNext) { if ((deviceType == Device_None || deviceType == devDesc->Type) && devDesc->MatchDevice(path)) return devDesc; } return NULL; } Ptr DeviceManagerImpl::FindHIDDevice(const HIDDeviceDesc& hidDevDesc, bool created) { Lock::Locker deviceLock(GetLock()); DeviceCreateDesc* devDesc; for (devDesc = Devices.GetFirst(); !Devices.IsNull(devDesc); devDesc = devDesc->pNext) { if (created) { // Search for matching device that is created if (devDesc->MatchHIDDevice(hidDevDesc) && devDesc->pDevice) return devDesc; } else { // Search for any matching device if (devDesc->MatchHIDDevice(hidDevDesc)) return devDesc; } } return NULL; } void DeviceManagerImpl::DetectHIDDevice(const HIDDeviceDesc& hidDevDesc) { Lock::Locker deviceLock(GetLock()); DeviceFactory* factory = Factories.GetFirst(); while(!Factories.IsNull(factory)) { if (factory->DetectHIDDevice(this, hidDevDesc)) break; factory = factory->pNext; } } // Enumerates devices for a particular factory. Void DeviceManagerImpl::EnumerateFactoryDevices(DeviceFactory* factory) { class FactoryEnumerateVisitor : public DeviceFactory::EnumerateVisitor { DeviceManagerImpl* pManager; DeviceFactory* pFactory; public: FactoryEnumerateVisitor(DeviceManagerImpl* manager, DeviceFactory* factory) : pManager(manager), pFactory(factory) { } virtual void Visit(const DeviceCreateDesc& createDesc) { pManager->AddDevice_NeedsLock(createDesc); } }; FactoryEnumerateVisitor newDeviceVisitor(this, factory); factory->EnumerateDevices(newDeviceVisitor); return 0; } DeviceEnumerator<> DeviceManagerImpl::EnumerateDevicesEx(const DeviceEnumerationArgs& args) { Lock::Locker deviceLock(GetLock()); if (Devices.IsEmpty()) return DeviceEnumerator<>(); DeviceCreateDesc* firstDeviceDesc = Devices.GetFirst(); DeviceEnumerator<> e = enumeratorFromHandle(DeviceHandle(firstDeviceDesc), args); if (!args.MatchRule(firstDeviceDesc->Type, firstDeviceDesc->Enumerated)) { e.Next(); } return e; } //------------------------------------------------------------------------------------- // ***** DeviceCommon void DeviceCommon::DeviceAddRef() { RefCount++; } void DeviceCommon::DeviceRelease() { while(1) { UInt32 refCount = RefCount; OVR_ASSERT(refCount > 0); if (refCount == 1) { DeviceManagerImpl* manager = pCreateDesc->GetManagerImpl(); ThreadCommandQueue* queue = manager->GetThreadQueue(); // Enqueue ReleaseDevice for {1 -> 0} transition with no wait. // We pass our reference ownership into the queue to destroy. // It's in theory possible for another thread to re-steal our device reference, // but that is checked for atomically in DeviceManagerImpl::ReleaseDevice. if (!queue->PushCall(manager, &DeviceManagerImpl::ReleaseDevice_MgrThread, pCreateDesc->pDevice)) { // PushCall shouldn't fail because background thread runs while manager is // alive and we are holding Manager alive through pParent chain. OVR_ASSERT(false); } // Warning! At his point everything, including manager, may be dead. break; } else if (RefCount.CompareAndSet_NoSync(refCount, refCount-1)) { break; } } } //------------------------------------------------------------------------------------- // ***** DeviceCreateDesc void DeviceCreateDesc::AddRef() { // Technically, HandleCount { 0 -> 1 } transition can only happen during Lock, // but we leave this to caller to worry about (happens during enumeration). HandleCount++; } void DeviceCreateDesc::Release() { while(1) { UInt32 handleCount = HandleCount; // HandleCount must obviously be >= 1, since we are releasing it. OVR_ASSERT(handleCount > 0); // {1 -> 0} transition may cause us to be destroyed, so require a lock. if (handleCount == 1) { Ptr lockKeepAlive; Lock::Locker deviceLockScope(GetLock()); if (!HandleCount.CompareAndSet_NoSync(handleCount, 0)) continue; OVR_ASSERT(pDevice == 0); // Destroy *this if the manager was destroyed already, or Enumerated // is false (device no longer available). if (!GetManagerImpl() || !Enumerated) { lockKeepAlive = pLock; // Remove from manager list (only matters for !Enumerated). if (pNext) { RemoveNode(); pNext = pPrev = 0; } delete this; } // Available DeviceCreateDesc may survive with { HandleCount == 0 }, // in case it might be enumerated again later. break; } else if (HandleCount.CompareAndSet_NoSync(handleCount, handleCount-1)) { break; } } } HMDDevice* HMDDevice::Disconnect(SensorDevice* psensor) { if (!psensor) return NULL; OVR::DeviceManager* manager = GetManager(); if (manager) { //DeviceManagerImpl* mgrImpl = static_cast(manager); Ptr desc = getDeviceCommon()->pCreateDesc; if (desc) { class Visitor : public DeviceFactory::EnumerateVisitor { Ptr Desc; public: Visitor(DeviceCreateDesc* desc) : Desc(desc) {} virtual void Visit(const DeviceCreateDesc& createDesc) { Lock::Locker lock(Desc->GetLock()); Desc->UpdateMatchedCandidate(createDesc); } } visitor(desc); //SensorDeviceImpl* sImpl = static_cast(psensor); SensorDisplayInfoImpl displayInfo; if (psensor->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) { displayInfo.Unpack(); // If we got display info, try to match / create HMDDevice as well // so that sensor settings give preference. if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) { SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, visitor); } } } } return this; } bool HMDDevice::IsDisconnected() const { OVR::HMDInfo info; GetDeviceInfo(&info); // if strlen(info.DisplayDeviceName) == 0 then // this HMD is 'fake' (created using sensor). return (strlen(info.DisplayDeviceName) == 0); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceImpl.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceImpl.h new file mode 100644 index 0000000..c6925ec --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceImpl.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_DeviceImpl.h Content : Partial back-end independent implementation of Device interfaces Created : October 10, 2012 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_DeviceImpl_h #define OVR_DeviceImpl_h #include "OVR_Device.h" #include "Kernel/OVR_Atomic.h" #include "Kernel/OVR_Log.h" #include "Kernel/OVR_System.h" #include "Kernel/OVR_Threads.h" #include "OVR_ThreadCommandQueue.h" #include "OVR_HIDDevice.h" namespace OVR { class DeviceManagerImpl; class DeviceFactory; enum { Oculus_VendorId = 0x2833, Device_Tracker_ProductId = 0x0001, Device_Tracker2_ProductId = 0x0021, Device_KTracker_ProductId = 0x0010, }; // Wrapper for MessageHandler that includes synchronization logic. class MessageHandlerRef { enum { MaxHandlersCount = 4 }; public: MessageHandlerRef(DeviceBase* device); ~MessageHandlerRef(); bool HasHandlers() const { return HandlersCount > 0; }; void AddHandler(MessageHandler* handler); // returns false if the handler is not found bool RemoveHandler(MessageHandler* handler); // Not-thread-safe version void AddHandler_NTS(MessageHandler* handler); void Call(const Message& msg); Lock* GetLock() const { return pLock; } DeviceBase* GetDevice() const { return pDevice; } private: Lock* pLock; // Cached global handler lock. DeviceBase* pDevice; int HandlersCount; MessageHandler* pHandlers[MaxHandlersCount]; bool removeHandler(int idx); }; //------------------------------------------------------------------------------------- // DeviceManagerLock is a synchronization lock used by DeviceManager for Devices // and is allocated separately for potentially longer lifetime. // // DeviceManagerLock is used for all of the following: // - Adding/removing devices // - Reporting manager lifetime (pManager != 0) for DeviceHandles // - Protecting device creation/shutdown. class DeviceManagerLock : public RefCountBase { public: Lock CreateLock; DeviceManagerImpl* pManager; DeviceManagerLock() : pManager(0) { } }; // DeviceCreateDesc provides all of the information needed to create any device, a derived // instance of this class is created by DeviceFactory during enumeration. // - DeviceCreateDesc may or may not be a part of DeviceManager::Devices list (check pNext != 0). // - Referenced and kept alive by DeviceHandle. class DeviceCreateDesc : public ListNode, public NewOverrideBase { void operator = (const DeviceCreateDesc&) { } // Assign not supported; suppress MSVC warning. public: DeviceCreateDesc(DeviceFactory* factory, DeviceType type) : pFactory(factory), Type(type), pLock(0), HandleCount(0), pDevice(0), Enumerated(true) { pNext = pPrev = 0; } virtual ~DeviceCreateDesc() { OVR_ASSERT(!pDevice); if (pNext) RemoveNode(); } DeviceManagerImpl* GetManagerImpl() const { return pLock->pManager; } Lock* GetLock() const { return &pLock->CreateLock; } // DeviceCreateDesc reference counting is tied to Devices list management, // see comments for HandleCount. void AddRef(); void Release(); // *** Device creation/matching Interface // Cloning copies us to an allocated object when new device is enumerated. virtual DeviceCreateDesc* Clone() const = 0; // Creates a new device instance without Initializing it; the // later is done my Initialize()/Shutdown() methods of the device itself. virtual DeviceBase* NewDeviceInstance() = 0; // Override to return device-specific info. virtual bool GetDeviceInfo(DeviceInfo* info) const = 0; enum MatchResult { Match_None, Match_Found, Match_Candidate }; // Override to return Match_Found if descriptor matches our device. // Match_Candidate can be returned, with pcandicate update, if this may be a match // but more searching is necessary. If this is the case UpdateMatchedCandidate will be called. virtual MatchResult MatchDevice(const DeviceCreateDesc& other, DeviceCreateDesc** pcandidate) const = 0; // Called for matched candidate after all potential matches are iterated. // Used to update HMDevice creation arguments from Sensor. // Optional return param 'newDeviceFlag' will be set to true if the // 'desc' refers to a new device; false, otherwise. // Return 'false' to create new object, 'true' if done with this argument. virtual bool UpdateMatchedCandidate( const DeviceCreateDesc& desc, bool* newDeviceFlag = NULL) { OVR_UNUSED2(desc, newDeviceFlag); return false; } // Matches HID device to the descriptor. virtual bool MatchHIDDevice(const HIDDeviceDesc&) const { return false; } // Matches device by path. virtual bool MatchDevice(const String& /*path*/) { return false; } //protected: DeviceFactory* const pFactory; const DeviceType Type; // List in which this descriptor lives. pList->CreateLock required if added/removed. Ptr pLock; // Strong references to us: Incremented by Device, DeviceHandles & Enumerators. // May be 0 if device not created and there are no handles. // Following transitions require pList->CreateLock: // {1 -> 0}: May delete & remove handle if no longer available. // {0 -> 1}: Device creation is only possible if manager is still alive. AtomicInt HandleCount; // If not null, points to our created device instance. Modified during lock only. DeviceBase* pDevice; // True if device is marked as available during enumeration. bool Enumerated; }; // Common data present in the implementation of every DeviceBase. // Injected by DeviceImpl. class DeviceCommon { public: AtomicInt RefCount; Ptr pCreateDesc; Ptr pParent; volatile bool ConnectedFlag; MessageHandlerRef HandlerRef; DeviceCommon(DeviceCreateDesc* createDesc, DeviceBase* device, DeviceBase* parent) : RefCount(1), pCreateDesc(createDesc), pParent(parent), ConnectedFlag(true), HandlerRef(device) { } virtual ~DeviceCommon() {} // Device reference counting delegates to Manager thread to actually kill devices. void DeviceAddRef(); void DeviceRelease(); Lock* GetLock() const { return pCreateDesc->GetLock(); } virtual bool Initialize(DeviceBase* parent) = 0; virtual void Shutdown() = 0; }; //------------------------------------------------------------------------------------- // DeviceImpl address DeviceRecord implementation to a device base class B. // B must be derived form DeviceBase. template class DeviceImpl : public B, public DeviceCommon { public: DeviceImpl(DeviceCreateDesc* createDesc, DeviceBase* parent) : DeviceCommon(createDesc, getThis(), parent) { } // Convenience method to avoid manager access typecasts. DeviceManagerImpl* GetManagerImpl() const { return pCreateDesc->pLock->pManager; } // Inline to avoid warnings. DeviceImpl* getThis() { return this; } // Common implementation delegate to avoid virtual inheritance and dynamic casts. virtual DeviceCommon* getDeviceCommon() const { return (DeviceCommon*)this; } /* virtual void AddRef() { pCreateDesc->DeviceAddRef(); } virtual void Release() { pCreateDesc->DeviceRelease(); } virtual DeviceBase* GetParent() const { return pParent.GetPtr(); } virtual DeviceManager* GetManager() const { return pCreateDesc->pLock->pManager;} virtual void SetMessageHandler(MessageHandler* handler) { HanderRef.SetHandler(handler); } virtual MessageHandler* GetMessageHandler() const { return HanderRef.GetHandler(); } virtual DeviceType GetType() const { return pCreateDesc->Type; } virtual DeviceType GetType() const { return pCreateDesc->Type; } */ }; //------------------------------------------------------------------------------------- // ***** DeviceFactory // DeviceFactory is maintained in DeviceManager for each separately-enumerable // device type; factories allow separation of unrelated enumeration code. class DeviceFactory : public ListNode, public NewOverrideBase { public: DeviceFactory() : pManager(0) { pNext = pPrev = 0; } virtual ~DeviceFactory() { } DeviceManagerImpl* GetManagerImpl() { return pManager; } // Notifiers called when we are added to/removed from a device. virtual bool AddedToManager(DeviceManagerImpl* manager) { OVR_ASSERT(pManager == 0); pManager = manager; return true; } virtual void RemovedFromManager() { pManager = 0; } // *** Device Enumeration/Creation Support // Passed to EnumerateDevices to be informed of every device detected. class EnumerateVisitor { public: virtual void Visit(const DeviceCreateDesc& createDesc) = 0; }; // Enumerates factory devices by notifying EnumerateVisitor about every // device that is present. virtual void EnumerateDevices(EnumerateVisitor& visitor) = 0; // Matches vendorId/productId pair with the factory; returns 'true' // if the factory can handle the device. virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const { OVR_UNUSED2(vendorId, productId); return false; } // Detects the HID device and adds the DeviceCreateDesc into Devices list, if // the device belongs to this factory. Returns 'false', if not. virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) { OVR_UNUSED2(pdevMgr, desc); return false; } protected: DeviceManagerImpl* pManager; }; //------------------------------------------------------------------------------------- // ***** DeviceManagerImpl // DeviceManagerImpl is a partial default DeviceManager implementation that // maintains a list of devices and supports their enumeration. class DeviceManagerImpl : public DeviceImpl, public ThreadCommandQueue { public: DeviceManagerImpl(); ~DeviceManagerImpl(); // Constructor helper function to create Descriptor and manager lock during initialization. static DeviceCreateDesc* CreateManagerDesc(); // DeviceManagerImpl provides partial implementation of Initialize/Shutdown that must // be called by the platform-specific derived class. virtual bool Initialize(DeviceBase* parent); virtual void Shutdown(); // Every DeviceManager has an associated profile manager, which is used to store // user settings that may affect device behavior. virtual ProfileManager* GetProfileManager() const { return pProfileManager.GetPtr(); } // Override to return ThreadCommandQueue implementation used to post commands // to the background device manager thread (that must be created by Initialize). virtual ThreadCommandQueue* GetThreadQueue() = 0; // Returns the thread id of the DeviceManager. virtual ThreadId GetThreadId() const = 0; virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); // void AddFactory(DeviceFactory* factory) { // This lock is only needed if we call AddFactory after manager thread creation. Lock::Locker scopeLock(GetLock()); Factories.PushBack(factory); factory->AddedToManager(this); } void CallOnDeviceAdded(DeviceCreateDesc* desc) { HandlerRef.Call(MessageDeviceStatus(Message_DeviceAdded, this, DeviceHandle(desc))); } void CallOnDeviceRemoved(DeviceCreateDesc* desc) { HandlerRef.Call(MessageDeviceStatus(Message_DeviceRemoved, this, DeviceHandle(desc))); } // Helper to access Common data for a device. static DeviceCommon* GetDeviceCommon(DeviceBase* device) { return device->getDeviceCommon(); } // Background-thread callbacks for DeviceCreation/Release. These DeviceBase* CreateDevice_MgrThread(DeviceCreateDesc* createDesc, DeviceBase* parent = 0); Void ReleaseDevice_MgrThread(DeviceBase* device); // Calls EnumerateDevices() on all factories virtual Void EnumerateAllFactoryDevices(); // Enumerates devices for a particular factory. virtual Void EnumerateFactoryDevices(DeviceFactory* factory); virtual HIDDeviceManager* GetHIDDeviceManager() const { return HidDeviceManager; } // Adds device (DeviceCreateDesc*) into Devices. Returns NULL, // if unsuccessful or device is already in the list. virtual Ptr AddDevice_NeedsLock(const DeviceCreateDesc& createDesc); // Finds a device descriptor by path and optional type. Ptr FindDevice(const String& path, DeviceType = Device_None); // Finds HID device by HIDDeviceDesc. Ptr FindHIDDevice(const HIDDeviceDesc&, bool created); void DetectHIDDevice(const HIDDeviceDesc&); // Manager Lock-protected list of devices. List Devices; // Factories used to detect and manage devices. List Factories; protected: Ptr HidDeviceManager; Ptr pProfileManager; }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceMessages.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceMessages.h new file mode 100644 index 0000000..8cf5821 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_DeviceMessages.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_DeviceMessages.h Content : Definition of messages generated by devices Created : February 5, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_DeviceMessages_h #define OVR_DeviceMessages_h #include "OVR_DeviceConstants.h" #include "OVR_DeviceHandle.h" #include "Kernel/OVR_Math.h" #include "Kernel/OVR_Array.h" #include "Kernel/OVR_Color.h" #include "Kernel/OVR_String.h" namespace OVR { class DeviceBase; class DeviceHandle; class String; #define OVR_MESSAGETYPE(devName, msgIndex) ((Device_##devName << 8) | msgIndex) // MessageType identifies the structure of the Message class; based on the message, // casting can be used to obtain the exact value. enum MessageType { // Used for unassigned message types. Message_None = 0, // Device Manager Messages Message_DeviceAdded = OVR_MESSAGETYPE(Manager, 0), // A new device is detected by manager. Message_DeviceRemoved = OVR_MESSAGETYPE(Manager, 1), // Existing device has been plugged/unplugged. // Sensor Messages Message_BodyFrame = OVR_MESSAGETYPE(Sensor, 0), // Emitted by sensor at regular intervals. Message_ExposureFrame = OVR_MESSAGETYPE(Sensor, 1), Message_PixelRead = OVR_MESSAGETYPE(Sensor, 2), // Latency Tester Messages Message_LatencyTestSamples = OVR_MESSAGETYPE(LatencyTester, 0), Message_LatencyTestColorDetected = OVR_MESSAGETYPE(LatencyTester, 1), Message_LatencyTestStarted = OVR_MESSAGETYPE(LatencyTester, 2), Message_LatencyTestButton = OVR_MESSAGETYPE(LatencyTester, 3), Message_CameraFrame = OVR_MESSAGETYPE(Camera, 0), Message_CameraAdded = OVR_MESSAGETYPE(Camera, 1), }; //------------------------------------------------------------------------------------- // Base class for all messages. class Message { public: Message(MessageType type = Message_None, DeviceBase* pdev = 0) : Type(type), pDevice(pdev) { } MessageType Type; // What kind of message this is. DeviceBase* pDevice; // Device that emitted the message. }; // Sensor BodyFrame notification. // Sensor uses Right-Handed coordinate system to return results, with the following // axis definitions: // - Y Up positive // - X Right Positive // - Z Back Positive // Rotations a counter-clockwise (CCW) while looking in the negative direction // of the axis. This means they are interpreted as follows: // - Roll is rotation around Z, counter-clockwise (tilting left) in XY plane. // - Yaw is rotation around Y, positive for turning left. // - Pitch is rotation around X, positive for pitching up. //------------------------------------------------------------------------------------- // ***** Sensor class MessageBodyFrame : public Message { public: MessageBodyFrame(DeviceBase* dev) : Message(Message_BodyFrame, dev), Temperature(0.0f), TimeDelta(0.0f) { } Vector3f Acceleration; // Acceleration in m/s^2. Vector3f RotationRate; // Angular velocity in rad/s. Vector3f MagneticField; // Magnetic field strength in Gauss. float Temperature; // Temperature reading on sensor surface, in degrees Celsius. float TimeDelta; // Time passed since last Body Frame, in seconds. // The absolute time from the host computers perspective that the message should be // interpreted as. This is based on incoming timestamp and processed by a filter // that syncs the clocks while attempting to keep the distance between messages // device clock matching. // // Integration should use TimeDelta, but prediction into the future should derive // the delta time from PredictToSeconds - AbsoluteTimeSeconds. // // This value will generally be <= the return from a call to ovr_GetTimeInSeconds(), // but could be greater by under 1 ms due to system time update interrupt delays. // double AbsoluteTimeSeconds; }; // Sent when we receive a device status changes (e.g.: // Message_DeviceAdded, Message_DeviceRemoved). class MessageDeviceStatus : public Message { public: MessageDeviceStatus(MessageType type, DeviceBase* dev, const DeviceHandle &hdev) : Message(type, dev), Handle(hdev) { } DeviceHandle Handle; }; class MessageExposureFrame : public Message { public: MessageExposureFrame(DeviceBase* dev) : Message(Message_ExposureFrame, dev), CameraPattern(0), CameraFrameCount(0), CameraTimeSeconds(0) { } UByte CameraPattern; UInt32 CameraFrameCount; double CameraTimeSeconds; }; class MessagePixelRead : public Message { public: MessagePixelRead(DeviceBase* dev) : Message(Message_PixelRead, dev), PixelReadValue(0), SensorTimeSeconds(0), FrameTimeSeconds(0) { } UByte PixelReadValue; UInt32 RawSensorTime; UInt32 RawFrameTime; double SensorTimeSeconds; double FrameTimeSeconds; }; //------------------------------------------------------------------------------------- // ***** Latency Tester // Sent when we receive Latency Tester samples. class MessageLatencyTestSamples : public Message { public: MessageLatencyTestSamples(DeviceBase* dev) : Message(Message_LatencyTestSamples, dev) { } Array Samples; }; // Sent when a Latency Tester 'color detected' event occurs. class MessageLatencyTestColorDetected : public Message { public: MessageLatencyTestColorDetected(DeviceBase* dev) : Message(Message_LatencyTestColorDetected, dev) { } UInt16 Elapsed; Color DetectedValue; Color TargetValue; }; // Sent when a Latency Tester 'change color' event occurs. class MessageLatencyTestStarted : public Message { public: MessageLatencyTestStarted(DeviceBase* dev) : Message(Message_LatencyTestStarted, dev) { } Color TargetValue; }; // Sent when a Latency Tester 'button' event occurs. class MessageLatencyTestButton : public Message { public: MessageLatencyTestButton(DeviceBase* dev) : Message(Message_LatencyTestButton, dev) { } }; //------------------------------------------------------------------------------------- // ***** Camera // Sent by camera, frame. class MessageCameraFrame : public Message { public: MessageCameraFrame(DeviceBase* dev) : Message(Message_CameraFrame, dev), CameraHandle(NULL), pFrameData(NULL) { LostFrames = 0; } void SetInfo(UInt32 frameNumber, double timeSeconds, UInt32 width, UInt32 height, UInt32 format) { FrameNumber = frameNumber; ArrivalTimeSeconds = timeSeconds; Width = width; Height = height; Format = format; } void SetData(const UByte* pdata, UInt32 sizeInBytes) { pFrameData = pdata; FrameSizeInBytes = sizeInBytes; } UInt32 FrameNumber; // an index of the frame double ArrivalTimeSeconds; // frame time in seconds, as recorded by the host computer const UByte* pFrameData; // a ptr to frame data. UInt32 FrameSizeInBytes; // size of the data in the pFrameData. UInt32 Width, Height; // width & height in pixels. UInt32 Format; // format of pixel, see CameraDevice::PixelFormat enum UInt32 LostFrames; // number of lost frames before this frame String DeviceIdentifier; // identifies the device sensing the message UInt32* CameraHandle; // Identifies the camera object associated with this frame }; // Sent when a new camera is connected class MessageCameraAdded : public Message { public: MessageCameraAdded(DeviceBase* dev) : Message(Message_CameraAdded, dev) { } MessageCameraAdded(UInt32* cam) : Message(Message_CameraAdded, NULL), CameraHandle(cam) { } UInt32* CameraHandle; // Identifies the camera object associated with this frame }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDevice.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDevice.h new file mode 100644 index 0000000..163b7b8 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDevice.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_HIDDevice.h Content : Cross platform HID device interface. Created : February 22, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_HIDDevice_h #define OVR_HIDDevice_h #include "OVR_HIDDeviceBase.h" #include "Kernel/OVR_RefCount.h" #include "Kernel/OVR_String.h" #include "Kernel/OVR_Timer.h" namespace OVR { class HIDDevice; class DeviceManager; // HIDDeviceDesc contains interesting attributes of a HID device, including a Path // that can be used to create it. struct HIDDeviceDesc { UInt16 VendorId; UInt16 ProductId; UInt16 VersionNumber; UInt16 Usage; UInt16 UsagePage; String Path; // Platform specific. String Manufacturer; String Product; String SerialNumber; }; // HIDEnumerateVisitor exposes a Visit interface called for every detected device // by HIDDeviceManager::Enumerate. class HIDEnumerateVisitor { public: // Should return true if we are interested in supporting // this HID VendorId and ProductId pair. virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) { OVR_UNUSED2(vendorId, productId); return true; } // Override to get notified about available device. Will only be called for // devices that matched MatchVendorProduct. virtual void Visit(HIDDevice&, const HIDDeviceDesc&) { } }; //------------------------------------------------------------------------------------- // ***** HIDDeviceManager // Internal manager for enumerating and opening HID devices. // If an OVR::DeviceManager is created then an OVR::HIDDeviceManager will automatically be created and can be accessed from the // DeviceManager by calling 'GetHIDDeviceManager()'. When using HIDDeviceManager in standalone mode, the client must call // 'Create' below. class HIDDeviceManager : public RefCountBase { public: // Creates a new HIDDeviceManager. Only one instance of HIDDeviceManager should be created at a time. static HIDDeviceManager* Create(Ptr& deviceManager); // Enumerate HID devices using a HIDEnumerateVisitor derived visitor class. virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor) = 0; // Open a HID device with the specified path. virtual HIDDevice* Open(const String& path) = 0; protected: HIDDeviceManager() { } }; //------------------------------------------------------------------------------------- // ***** HIDDevice // HID device object. This is designed to be operated in synchronous // and asynchronous modes. With no handler set, input messages will be // stored and can be retrieved by calling 'Read' or 'ReadBlocking'. class HIDDevice : public RefCountBase, public HIDDeviceBase { public: HIDDevice() : Handler(NULL) { } virtual ~HIDDevice() {} virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0; virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0; // Not yet implemented. /* virtual bool Write(UByte* data, UInt32 length) = 0; virtual bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS) = 0; virtual bool ReadBlocking(UByte* pData, UInt32 length) = 0; */ class HIDHandler { public: virtual void OnInputReport(UByte* pData, UInt32 length) { OVR_UNUSED2(pData, length); } virtual double OnTicks(double tickSeconds) { OVR_UNUSED1(tickSeconds); return 1000.0 ; } enum HIDDeviceMessageType { HIDDeviceMessage_DeviceAdded = 0, HIDDeviceMessage_DeviceRemoved = 1 }; virtual void OnDeviceMessage(HIDDeviceMessageType messageType) { OVR_UNUSED1(messageType); } }; void SetHandler(HIDHandler* handler) { Handler = handler; } protected: HIDHandler* Handler; }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDeviceBase.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDeviceBase.h new file mode 100644 index 0000000..fd1a80b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDeviceBase.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_HIDDeviceBase.h Content : Definition of HID device interface. Created : March 11, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_HIDDeviceBase_h #define OVR_HIDDeviceBase_h #include "Kernel/OVR_Types.h" namespace OVR { //------------------------------------------------------------------------------------- // ***** HIDDeviceBase // Base interface for HID devices. class HIDDeviceBase { public: virtual ~HIDDeviceBase() { } virtual bool SetFeatureReport(UByte* data, UInt32 length) = 0; virtual bool GetFeatureReport(UByte* data, UInt32 length) = 0; }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDeviceImpl.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDeviceImpl.h new file mode 100644 index 0000000..8cb57b4 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_HIDDeviceImpl.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_HIDDeviceImpl.h Content : Implementation of HIDDevice. Created : March 7, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_HIDDeviceImpl_h #define OVR_HIDDeviceImpl_h //#include "OVR_Device.h" #include "OVR_DeviceImpl.h" namespace OVR { //------------------------------------------------------------------------------------- class HIDDeviceCreateDesc : public DeviceCreateDesc { public: HIDDeviceCreateDesc(DeviceFactory* factory, DeviceType type, const HIDDeviceDesc& hidDesc) : DeviceCreateDesc(factory, type), HIDDesc(hidDesc) { } HIDDeviceCreateDesc(const HIDDeviceCreateDesc& other) : DeviceCreateDesc(other.pFactory, other.Type), HIDDesc(other.HIDDesc) { } virtual bool MatchDevice(const String& path) { // should it be case insensitive? return HIDDesc.Path.CompareNoCase(path) == 0; } HIDDeviceDesc HIDDesc; }; //------------------------------------------------------------------------------------- template class HIDDeviceImpl : public DeviceImpl, public HIDDevice::HIDHandler { public: HIDDeviceImpl(HIDDeviceCreateDesc* createDesc, DeviceBase* parent) : DeviceImpl(createDesc, parent) { } // HIDDevice::Handler interface. virtual void OnDeviceMessage(HIDDeviceMessageType messageType) { MessageType handlerMessageType; switch (messageType) { case HIDDeviceMessage_DeviceAdded: handlerMessageType = Message_DeviceAdded; DeviceImpl::ConnectedFlag = true; break; case HIDDeviceMessage_DeviceRemoved: handlerMessageType = Message_DeviceRemoved; DeviceImpl::ConnectedFlag = false; break; default: OVR_ASSERT(0); return; } // Do device notification. MessageDeviceStatus status(handlerMessageType, this, OVR::DeviceHandle(this->pCreateDesc)); this->HandlerRef.Call(status); // Do device manager notification. DeviceManagerImpl* manager = this->GetManagerImpl(); switch (handlerMessageType) { case Message_DeviceAdded: manager->CallOnDeviceAdded(this->pCreateDesc); break; case Message_DeviceRemoved: manager->CallOnDeviceRemoved(this->pCreateDesc); break; default:; } } virtual bool Initialize(DeviceBase* parent) { // Open HID device. HIDDeviceDesc& hidDesc = *getHIDDesc(); HIDDeviceManager* pManager = GetHIDDeviceManager(); HIDDevice* device = pManager->Open(hidDesc.Path); if (!device) { return false; } InternalDevice = *device; InternalDevice->SetHandler(this); // AddRef() to parent, forcing chain to stay alive. DeviceImpl::pParent = parent; return true; } virtual void Shutdown() { InternalDevice->SetHandler(NULL); DeviceImpl::pParent.Clear(); } DeviceManager* GetDeviceManager() { return DeviceImpl::pCreateDesc->GetManagerImpl(); } HIDDeviceManager* GetHIDDeviceManager() { return DeviceImpl::pCreateDesc->GetManagerImpl()->GetHIDDeviceManager(); } bool SetFeatureReport(UByte* data, UInt32 length) { // Push call with wait. bool result = false; ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::setFeatureReport, &result, data, length)) return false; return result; } bool setFeatureReport(UByte* data, UInt32 length) { return InternalDevice->SetFeatureReport(data, length); } bool GetFeatureReport(UByte* data, UInt32 length) { bool result = false; ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); if (!pQueue->PushCallAndWaitResult(this, &HIDDeviceImpl::getFeatureReport, &result, data, length)) return false; return result; } bool getFeatureReport(UByte* data, UInt32 length) { return InternalDevice->GetFeatureReport(data, length); } UByte GetDeviceInterfaceVersion() { UInt16 versionNumber = getHIDDesc()->VersionNumber; // Our interface and hardware versions are represented as two BCD digits each. // Interface version is in the last two digits. UByte interfaceVersion = (UByte) ((versionNumber & 0x000F) >> 0) * 1 + ((versionNumber & 0x00F0) >> 4) * 10; return interfaceVersion; } protected: HIDDevice* GetInternalDevice() const { return InternalDevice; } HIDDeviceDesc* getHIDDesc() const { return &getCreateDesc()->HIDDesc; } HIDDeviceCreateDesc* getCreateDesc() const { return (HIDDeviceCreateDesc*) &(*DeviceImpl::pCreateDesc); } private: Ptr InternalDevice; }; } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_JSON.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_JSON.cpp new file mode 100644 index 0000000..d1ce3f9 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_JSON.cpp @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_JSON.h Content : JSON format reader and writer Created : April 9, 2013 Author : Brant Lewis Notes : The code is a derivative of the cJSON library written by Dave Gamble and subject to the following permissive copyright. Copyright (c) 2009 Dave Gamble Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 #include #include #include #include #include #include #include "OVR_JSON.h" #include "Kernel/OVR_SysFile.h" #include "Kernel/OVR_Log.h" namespace OVR { //----------------------------------------------------------------------------- // Create a new copy of a string static char* JSON_strdup(const char* str) { UPInt len = OVR_strlen(str) + 1; char* copy = (char*)OVR_ALLOC(len); if (!copy) return 0; memcpy(copy, str, len); return copy; } //----------------------------------------------------------------------------- // Render the number from the given item into a string. static char* PrintNumber(double d) { char *str; //double d=item->valuedouble; int valueint = (int)d; if (fabs(((double)valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) { str=(char*)OVR_ALLOC(21); // 2^64+1 can be represented in 21 chars. if (str) OVR_sprintf(str, 21, "%d", valueint); } else { str=(char*)OVR_ALLOC(64); // This is a nice tradeoff. if (str) { if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60) OVR_sprintf(str, 64, "%.0f", d); else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) OVR_sprintf(str, 64, "%e", d); else OVR_sprintf(str, 64, "%f", d); } } return str; } // Parse the input text into an un-escaped cstring, and populate item. static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; // Helper to assign error sting and return 0. const char* AssignError(const char** perror, const char *errorMessage) { if (perror) *perror = errorMessage; return 0; } //----------------------------------------------------------------------------- // ***** JSON Node class JSON::JSON(JSONItemType itemType) : Type(itemType), dValue(0.0) { } JSON::~JSON() { JSON* child = Children.GetFirst(); while (!Children.IsNull(child)) { child->RemoveNode(); child->Release(); child = Children.GetFirst(); } } //----------------------------------------------------------------------------- // Parse the input text to generate a number, and populate the result into item // Returns the text position after the parsed number const char* JSON::parseNumber(const char *num) { const char* num_start = num; double n=0, sign=1, scale=0; int subscale = 0, signsubscale = 1; // Could use sscanf for this? if (*num=='-') sign=-1,num++; // Has sign? if (*num=='0') num++; // is zero if (*num>='1' && *num<='9') { do { n=(n*10.0)+(*num++ -'0'); } while (*num>='0' && *num<='9'); // Number? } if (*num=='.' && num[1]>='0' && num[1]<='9') { num++; do { n=(n*10.0)+(*num++ -'0'); scale--; } while (*num>='0' && *num<='9'); // Fractional part? } if (*num=='e' || *num=='E') // Exponent? { num++; if (*num=='+') num++; else if (*num=='-') { signsubscale=-1; num++; // With sign? } while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); // Number? } // Number = +/- number.fraction * 10^+/- exponent n = sign*n*pow(10.0,(scale+subscale*signsubscale)); // Assign parsed value. Type = JSON_Number; dValue = n; Value.AssignString(num_start, num - num_start); return num; } // Parses a hex string up to the specified number of digits. // Returns the first character after the string. const char* ParseHex(unsigned* val, unsigned digits, const char* str) { *val = 0; for(unsigned digitCount = 0; digitCount < digits; digitCount++, str++) { unsigned v = *str; if ((v >= '0') && (v <= '9')) v -= '0'; else if ((v >= 'a') && (v <= 'f')) v = 10 + v - 'a'; else if ((v >= 'A') && (v <= 'F')) v = 10 + v - 'A'; else break; *val = *val * 16 + v; } return str; } //----------------------------------------------------------------------------- // Parses the input text into a string item and returns the text position after // the parsed string const char* JSON::parseString(const char* str, const char** perror) { const char* ptr = str+1; const char* p; char* ptr2; char* out; int len=0; unsigned uc, uc2; if (*str!='\"') { return AssignError(perror, "Syntax Error: Missing quote"); } while (*ptr!='\"' && *ptr && ++len) { if (*ptr++ == '\\') ptr++; // Skip escaped quotes. } // This is how long we need for the string, roughly. out=(char*)OVR_ALLOC(len+1); if (!out) return 0; ptr = str+1; ptr2= out; while (*ptr!='\"' && *ptr) { if (*ptr!='\\') { *ptr2++ = *ptr++; } else { ptr++; switch (*ptr) { case 'b': *ptr2++ = '\b'; break; case 'f': *ptr2++ = '\f'; break; case 'n': *ptr2++ = '\n'; break; case 'r': *ptr2++ = '\r'; break; case 't': *ptr2++ = '\t'; break; // Transcode utf16 to utf8. case 'u': // Get the unicode char. p = ParseHex(&uc, 4, ptr + 1); if (ptr != p) ptr = p - 1; if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; // Check for invalid. // UTF16 surrogate pairs. if (uc>=0xD800 && uc<=0xDBFF) { if (ptr[1]!='\\' || ptr[2]!='u') break; // Missing second-half of surrogate. p= ParseHex(&uc2, 4, ptr + 3); if (ptr != p) ptr = p - 1; if (uc2<0xDC00 || uc2>0xDFFF) break; // Invalid second-half of surrogate. uc = 0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); } len=4; if (uc<0x80) len=1; else if (uc<0x800) len=2; else if (uc<0x10000) len=3; ptr2+=len; switch (len) { case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; case 1: *--ptr2 = (char)(uc | firstByteMark[len]); } ptr2+=len; break; default: *ptr2++ = *ptr; break; } ptr++; } } *ptr2 = 0; if (*ptr=='\"') ptr++; // Make a copy of the string Value=out; OVR_FREE(out); Type=JSON_String; return ptr; } //----------------------------------------------------------------------------- // Render the string provided to an escaped version that can be printed. char* PrintString(const char* str) { const char *ptr; char *ptr2,*out; int len=0; unsigned char token; if (!str) return JSON_strdup(""); ptr=str; token=*ptr; while (token && ++len)\ { if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5; ptr++; token=*ptr; } int buff_size = len+3; out=(char*)OVR_ALLOC(buff_size); if (!out) return 0; ptr2 = out; ptr = str; *ptr2++ = '\"'; while (*ptr) { if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; else { *ptr2++='\\'; switch (token=*ptr++) { case '\\': *ptr2++='\\'; break; case '\"': *ptr2++='\"'; break; case '\b': *ptr2++='b'; break; case '\f': *ptr2++='f'; break; case '\n': *ptr2++='n'; break; case '\r': *ptr2++='r'; break; case '\t': *ptr2++='t'; break; default: OVR_sprintf(ptr2, buff_size - (ptr2-out), "u%04x",token); ptr2+=5; break; // Escape and print. } } } *ptr2++='\"'; *ptr2++=0; return out; } //----------------------------------------------------------------------------- // Utility to jump whitespace and cr/lf static const char* skip(const char* in) { while (in && *in && (unsigned char)*in<=' ') in++; return in; } //----------------------------------------------------------------------------- // Parses the supplied buffer of JSON text and returns a JSON object tree // The returned object must be Released after use JSON* JSON::Parse(const char* buff, const char** perror) { const char* end = 0; JSON* json = new JSON(); if (!json) { AssignError(perror, "Error: Failed to allocate memory"); return 0; } end = json->parseValue(skip(buff), perror); if (!end) { json->Release(); return NULL; } // parse failure. ep is set. return json; } //----------------------------------------------------------------------------- // This version works for buffers that are not null terminated strings. JSON* JSON::ParseBuffer(const char *buff, int len, const char** perror) { // Our JSON parser does not support length-based parsing, // so ensure it is null-terminated. char *termStr = new char[len + 1]; memcpy(termStr, buff, len); termStr[len] = '\0'; JSON *objJson = Parse(termStr, perror); delete[]termStr; return objJson; } //----------------------------------------------------------------------------- // Parser core - when encountering text, process appropriately. const char* JSON::parseValue(const char* buff, const char** perror) { if (perror) *perror = 0; if (!buff) return NULL; // Fail on null. if (!strncmp(buff,"null",4)) { Type = JSON_Null; return buff+4; } if (!strncmp(buff,"false",5)) { Type = JSON_Bool; Value = "false"; dValue = 0; return buff+5; } if (!strncmp(buff,"true",4)) { Type = JSON_Bool; Value = "true"; dValue = 1; return buff+4; } if (*buff=='\"') { return parseString(buff, perror); } if (*buff=='-' || (*buff>='0' && *buff<='9')) { return parseNumber(buff); } if (*buff=='[') { return parseArray(buff, perror); } if (*buff=='{') { return parseObject(buff, perror); } return AssignError(perror, "Syntax Error: Invalid syntax"); } //----------------------------------------------------------------------------- // Render a value to text. char* JSON::PrintValue(int depth, bool fmt) { char *out=0; switch (Type) { case JSON_Null: out = JSON_strdup("null"); break; case JSON_Bool: if (dValue == 0) out = JSON_strdup("false"); else out = JSON_strdup("true"); break; case JSON_Number: out = PrintNumber(dValue); break; case JSON_String: out = PrintString(Value); break; case JSON_Array: out = PrintArray(depth, fmt); break; case JSON_Object: out = PrintObject(depth, fmt); break; case JSON_None: OVR_ASSERT_LOG(false, ("Bad JSON type.")); break; } return out; } //----------------------------------------------------------------------------- // Build an array object from input text and returns the text position after // the parsed array const char* JSON::parseArray(const char* buff, const char** perror) { JSON *child; if (*buff!='[') { return AssignError(perror, "Syntax Error: Missing opening bracket"); } Type=JSON_Array; buff=skip(buff+1); if (*buff==']') return buff+1; // empty array. child = new JSON(); if (!child) return 0; // memory fail Children.PushBack(child); buff=skip(child->parseValue(skip(buff), perror)); // skip any spacing, get the buff. if (!buff) return 0; while (*buff==',') { JSON *new_item = new JSON(); if (!new_item) return AssignError(perror, "Error: Failed to allocate memory"); Children.PushBack(new_item); buff=skip(new_item->parseValue(skip(buff+1), perror)); if (!buff) return AssignError(perror, "Error: Failed to allocate memory"); } if (*buff==']') return buff+1; // end of array return AssignError(perror, "Syntax Error: Missing ending bracket"); } //----------------------------------------------------------------------------- // Render an array to text. The returned text must be freed char* JSON::PrintArray(int depth, bool fmt) { char **entries; char * out = 0,*ptr,*ret; SPInt len = 5; bool fail = false; // How many entries in the array? int numentries = GetItemCount(); if (!numentries) { out=(char*)OVR_ALLOC(3); if (out) OVR_strcpy(out, 3, "[]"); return out; } // Allocate an array to hold the values for each entries=(char**)OVR_ALLOC(numentries*sizeof(char*)); if (!entries) return 0; memset(entries,0,numentries*sizeof(char*)); //// Retrieve all the results: JSON* child = Children.GetFirst(); for (int i=0; iPrintValue(depth+1, fmt); entries[i]=ret; if (ret) len+=OVR_strlen(ret)+2+(fmt?1:0); else { fail = true; break; } child = Children.GetNext(child); } // If we didn't fail, try to malloc the output string if (!fail) out=(char*)OVR_ALLOC(len); // If that fails, we fail. if (!out) fail = true; // Handle failure. if (fail) { for (int i=0; iparseString(skip(buff), perror)); if (!buff) return 0; child->Name = child->Value; child->Value.Clear(); if (*buff!=':') { return AssignError(perror, "Syntax Error: Missing colon"); } buff=skip(child->parseValue(skip(buff+1), perror)); // skip any spacing, get the value. if (!buff) return 0; while (*buff==',') { child = new JSON(); if (!child) return 0; // memory fail Children.PushBack(child); buff=skip(child->parseString(skip(buff+1), perror)); if (!buff) return 0; child->Name=child->Value; child->Value.Clear(); if (*buff!=':') { return AssignError(perror, "Syntax Error: Missing colon"); } // fail! // Skip any spacing, get the value. buff=skip(child->parseValue(skip(buff+1), perror)); if (!buff) return 0; } if (*buff=='}') return buff+1; // end of array return AssignError(perror, "Syntax Error: Missing closing brace"); } //----------------------------------------------------------------------------- // Render an object to text. The returned string must be freed char* JSON::PrintObject(int depth, bool fmt) { char** entries = 0, **names = 0; char* out = 0; char* ptr, *ret, *str; SPInt len = 7, i = 0, j; bool fail = false; // Count the number of entries. int numentries = GetItemCount(); // Explicitly handle empty object case if (numentries == 0) { out=(char*)OVR_ALLOC(fmt?depth+3:3); if (!out) return 0; ptr=out; *ptr++='{'; if (fmt) { *ptr++='\n'; for (i=0;iName); entries[i++] = ret = child->PrintValue(depth, fmt); if (str && ret) { len += OVR_strlen(ret)+OVR_strlen(str)+2+(fmt?2+depth:0); } else { fail = true; break; } child = Children.GetNext(child); } // Try to allocate the output string if (!fail) out=(char*)OVR_ALLOC(len); if (!out) fail=true; // Handle failure if (fail) { for (i=0;ipNext) count++; return count; } JSON* JSON::GetItemByIndex(unsigned index) { unsigned i = 0; JSON* child = 0; if (!Children.IsEmpty()) { child = Children.GetFirst(); while (i < index) { if (Children.IsNull(child->pNext)) { child = 0; break; } child = child->pNext; i++; } } return child; } // Returns the child item with the given name or NULL if not found JSON* JSON::GetItemByName(const char* name) { JSON* child = 0; if (!Children.IsEmpty()) { child = Children.GetFirst(); while (OVR_strcmp(child->Name, name) != 0) { if (Children.IsNull(child->pNext)) { child = 0; break; } child = child->pNext; } } return child; } //----------------------------------------------------------------------------- // Adds a new item to the end of the child list void JSON::AddItem(const char *string, JSON *item) { if (!item) return; item->Name = string; Children.PushBack(item); } /* // Removes and frees the items at the given index void JSON::DeleteItem(unsigned int index) { unsigned int num_items = 0; JSON* child = Children.GetFirst(); while (!Children.IsNull(child) && num_items < index) { num_items++; child = Children.GetNext(child); } if (!Children.IsNull(child)) child->RemoveNode(); child->Release(); } } // Replaces and frees the item at the give index with the new item void JSON::ReplaceItem(unsigned int index, JSON* new_item) { unsigned int num_items = 0; JSON* child = Children.GetFirst(); while (!Children.IsNull(child) && num_items < index) { num_items++; child = Children.GetNext(child); } if (!Children.IsNull(child)) { child->ReplaceNodeWith(new_item); child->Release(); } } */ // Removes and frees the last child item void JSON::RemoveLast() { JSON* child = Children.GetLast(); if (!Children.IsNull(child)) { child->RemoveNode(); child->Release(); } } // Helper function to simplify creation of a typed object JSON* JSON::createHelper(JSONItemType itemType, double dval, const char* strVal) { JSON *item = new JSON(itemType); if (item) { item->dValue = dval; if (strVal) item->Value = strVal; } return item; } //----------------------------------------------------------------------------- // Get elements by name double JSON::GetNumberByName(const char *name, double defValue) { JSON* item = GetItemByName(name); if (!item || item->Type != JSON_Number) { return defValue; } else { return item->dValue; } } int JSON::GetIntByName(const char *name, int defValue) { JSON* item = GetItemByName(name); if (!item || item->Type != JSON_Number) { return defValue; } else { return (int)item->dValue; } } bool JSON::GetBoolByName(const char *name, bool defValue) { JSON* item = GetItemByName(name); if (!item || item->Type != JSON_Bool) { return defValue; } else { return (int)item->dValue != 0; } } String JSON::GetStringByName(const char *name, const String &defValue) { JSON* item = GetItemByName(name); if (!item || item->Type != JSON_String) { return defValue; } else { return item->Value; } } //----------------------------------------------------------------------------- // Adds an element to an array object type void JSON::AddArrayElement(JSON *item) { if (!item) return; Children.PushBack(item); } // Inserts an element into a valid array position void JSON::InsertArrayElement(int index, JSON *item) { if (!item) return; if (index == 0) { Children.PushFront(item); return; } JSON* iter = Children.GetFirst(); int i=0; while (iter && iInsertNodeBefore(item); else Children.PushBack(item); } // Returns the size of an array int JSON::GetArraySize() { if (Type == JSON_Array) return GetItemCount(); else return 0; } // Returns the number value an the give array index double JSON::GetArrayNumber(int index) { if (Type == JSON_Array) { JSON* number = GetItemByIndex(index); return number ? number->dValue : 0.0; } else { return 0; } } // Returns the string value at the given array index const char* JSON::GetArrayString(int index) { if (Type == JSON_Array) { JSON* number = GetItemByIndex(index); return number ? number->Value : 0; } else { return 0; } } JSON* JSON::Copy() { JSON* copy = new JSON(Type); copy->Name = Name; copy->Value = Value; copy->dValue = dValue; JSON* child = Children.GetFirst(); while (!Children.IsNull(child)) { copy->Children.PushBack(child->Copy()); child = Children.GetNext(child); } return copy; } //----------------------------------------------------------------------------- // Loads and parses the given JSON file pathname and returns a JSON object tree. // The returned object must be Released after use. JSON* JSON::Load(const char* path, const char** perror) { SysFile f; if (!f.Open(path, File::Open_Read, File::Mode_Read)) { AssignError(perror, "Failed to open file"); return NULL; } int len = f.GetLength(); UByte* buff = (UByte*)OVR_ALLOC(len + 1); int bytes = f.Read(buff, len); f.Close(); if (bytes == 0 || bytes != len) { OVR_FREE(buff); return NULL; } // Ensure the result is null-terminated since Parse() expects null-terminated input. buff[len] = '\0'; JSON* json = JSON::Parse((char*)buff, perror); OVR_FREE(buff); return json; } //----------------------------------------------------------------------------- // Serializes the JSON object and writes to the give file path bool JSON::Save(const char* path) { SysFile f; if (!f.Open(path, File::Open_Write | File::Open_Create | File::Open_Truncate, File::Mode_Write)) return false; char* text = PrintValue(0, true); if (text) { SPInt len = OVR_strlen(text); OVR_ASSERT(len <= (SPInt)(int)len); int bytes = f.Write((UByte*)text, (int)len); f.Close(); OVR_FREE(text); return (bytes == len); } else { return false; } } } \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_JSON.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_JSON.h new file mode 100644 index 0000000..9531319 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_JSON.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_JSON.h Content : JSON format reader and writer Created : April 9, 2013 Author : Brant Lewis Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_JSON_H #define OVR_JSON_H #include "Kernel/OVR_RefCount.h" #include "Kernel/OVR_String.h" #include "Kernel/OVR_List.h" namespace OVR { // JSONItemType describes the type of JSON item, specifying the type of // data that can be obtained from it. enum JSONItemType { JSON_None = 0, JSON_Null = 1, JSON_Bool = 2, JSON_Number = 3, JSON_String = 4, JSON_Array = 5, JSON_Object = 6 }; //----------------------------------------------------------------------------- // ***** JSON // JSON object represents a JSON node that can be either a root of the JSON tree // or a child item. Every node has a type that describes what is is. // New JSON trees are typically loaded JSON::Load or created with JSON::Parse. class JSON : public RefCountBase, public ListNode { protected: List Children; public: JSONItemType Type; // Type of this JSON node. String Name; // Name part of the {Name, Value} pair in a parent object. String Value; double dValue; public: ~JSON(); // *** Creation of NEW JSON objects static JSON* CreateObject() { return new JSON(JSON_Object);} static JSON* CreateNull() { return new JSON(JSON_Null); } static JSON* CreateArray() { return new JSON(JSON_Array); } static JSON* CreateBool(bool b) { return createHelper(JSON_Bool, b ? 1.0 : 0.0); } static JSON* CreateNumber(double num) { return createHelper(JSON_Number, num); } static JSON* CreateString(const char *s) { return createHelper(JSON_String, 0.0, s); } // Creates a new JSON object from parsing string. // Returns null pointer and fills in *perror in case of parse error. static JSON* Parse(const char* buff, const char** perror = 0); // This version works for buffers that are not null terminated strings. static JSON* ParseBuffer(const char *buff, int len, const char** perror = 0); // Loads and parses a JSON object from a file. // Returns 0 and assigns perror with error message on fail. static JSON* Load(const char* path, const char** perror = 0); // Saves a JSON object to a file. bool Save(const char* path); // *** Object Member Access // These provide access to child items of the list. bool HasItems() const { return Children.IsEmpty(); } // Returns first/last child item, or null if child list is empty JSON* GetFirstItem() { return (!Children.IsEmpty()) ? Children.GetFirst() : 0; } JSON* GetLastItem() { return (!Children.IsEmpty()) ? Children.GetLast() : 0; } // Counts the number of items in the object; these methods are inefficient. unsigned GetItemCount() const; JSON* GetItemByIndex(unsigned i); JSON* GetItemByName(const char* name); // Accessors by name double GetNumberByName(const char *name, double defValue = 0.0); int GetIntByName(const char *name, int defValue = 0); bool GetBoolByName(const char *name, bool defValue = false); String GetStringByName(const char *name, const String &defValue = ""); // Returns next item in a list of children; 0 if no more items exist. JSON* GetNextItem(JSON* item) { return Children.IsNull(item->pNext) ? 0 : item->pNext; } JSON* GetPrevItem(JSON* item) { return Children.IsNull(item->pPrev) ? 0 : item->pPrev; } // Child item access functions void AddItem(const char *string, JSON* item); void AddNullItem(const char* name) { AddItem(name, CreateNull()); } void AddBoolItem(const char* name, bool b) { AddItem(name, CreateBool(b)); } void AddNumberItem(const char* name, double n) { AddItem(name, CreateNumber(n)); } void AddStringItem(const char* name, const char* s) { AddItem(name, CreateString(s)); } // void ReplaceItem(unsigned index, JSON* new_item); // void DeleteItem(unsigned index); void RemoveLast(); // *** Array Element Access // Add new elements to the end of array. void AddArrayElement(JSON *item); void InsertArrayElement(int index, JSON* item); void AddArrayNumber(double n) { AddArrayElement(CreateNumber(n)); } void AddArrayString(const char* s) { AddArrayElement(CreateString(s)); } // Accessed array elements; currently inefficient. int GetArraySize(); double GetArrayNumber(int index); const char* GetArrayString(int index); JSON* Copy(); // Create a copy of this object protected: JSON(JSONItemType itemType = JSON_Object); static JSON* createHelper(JSONItemType itemType, double dval, const char* strVal = 0); // JSON Parsing helper functions. const char* parseValue(const char *buff, const char** perror); const char* parseNumber(const char *num); const char* parseArray(const char* value, const char** perror); const char* parseObject(const char* value, const char** perror); const char* parseString(const char* str, const char** perror); char* PrintValue(int depth, bool fmt); char* PrintObject(int depth, bool fmt); char* PrintArray(int depth, bool fmt); }; } #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_LatencyTestImpl.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_LatencyTestImpl.cpp new file mode 100644 index 0000000..5e183e6 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_LatencyTestImpl.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_LatencyTestImpl.cpp Content : Oculus Latency Tester device implementation. Created : March 7, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_LatencyTestImpl.h" #include "Kernel/OVR_Alg.h" namespace OVR { using namespace Alg; //------------------------------------------------------------------------------------- // ***** Oculus Latency Tester specific packet data structures enum { LatencyTester_VendorId = Oculus_VendorId, LatencyTester_ProductId = 0x0101, }; static void UnpackSamples(const UByte* buffer, UByte* r, UByte* g, UByte* b) { *r = buffer[0]; *g = buffer[1]; *b = buffer[2]; } // Messages we handle. enum LatencyTestMessageType { LatencyTestMessage_None = 0, LatencyTestMessage_Samples = 1, LatencyTestMessage_ColorDetected = 2, LatencyTestMessage_TestStarted = 3, LatencyTestMessage_Button = 4, LatencyTestMessage_Unknown = 0x100, LatencyTestMessage_SizeError = 0x101, }; struct LatencyTestSample { UByte Value[3]; }; struct LatencyTestSamples { UByte SampleCount; UInt16 Timestamp; LatencyTestSample Samples[20]; LatencyTestMessageType Decode(const UByte* buffer, int size) { if (size < 64) { return LatencyTestMessage_SizeError; } SampleCount = buffer[1]; Timestamp = DecodeUInt16(buffer + 2); for (UByte i = 0; i < SampleCount; i++) { UnpackSamples(buffer + 4 + (3 * i), &Samples[i].Value[0], &Samples[i].Value[1], &Samples[i].Value[2]); } return LatencyTestMessage_Samples; } }; struct LatencyTestSamplesMessage { LatencyTestMessageType Type; LatencyTestSamples Samples; }; bool DecodeLatencyTestSamplesMessage(LatencyTestSamplesMessage* message, UByte* buffer, int size) { memset(message, 0, sizeof(LatencyTestSamplesMessage)); if (size < 64) { message->Type = LatencyTestMessage_SizeError; return false; } switch (buffer[0]) { case LatencyTestMessage_Samples: message->Type = message->Samples.Decode(buffer, size); break; default: message->Type = LatencyTestMessage_Unknown; break; } return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); } struct LatencyTestColorDetected { UInt16 CommandID; UInt16 Timestamp; UInt16 Elapsed; UByte TriggerValue[3]; UByte TargetValue[3]; LatencyTestMessageType Decode(const UByte* buffer, int size) { if (size < 13) return LatencyTestMessage_SizeError; CommandID = DecodeUInt16(buffer + 1); Timestamp = DecodeUInt16(buffer + 3); Elapsed = DecodeUInt16(buffer + 5); memcpy(TriggerValue, buffer + 7, 3); memcpy(TargetValue, buffer + 10, 3); return LatencyTestMessage_ColorDetected; } }; struct LatencyTestColorDetectedMessage { LatencyTestMessageType Type; LatencyTestColorDetected ColorDetected; }; bool DecodeLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message, UByte* buffer, int size) { memset(message, 0, sizeof(LatencyTestColorDetectedMessage)); if (size < 13) { message->Type = LatencyTestMessage_SizeError; return false; } switch (buffer[0]) { case LatencyTestMessage_ColorDetected: message->Type = message->ColorDetected.Decode(buffer, size); break; default: message->Type = LatencyTestMessage_Unknown; break; } return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); } struct LatencyTestStarted { UInt16 CommandID; UInt16 Timestamp; UByte TargetValue[3]; LatencyTestMessageType Decode(const UByte* buffer, int size) { if (size < 8) return LatencyTestMessage_SizeError; CommandID = DecodeUInt16(buffer + 1); Timestamp = DecodeUInt16(buffer + 3); memcpy(TargetValue, buffer + 5, 3); return LatencyTestMessage_TestStarted; } }; struct LatencyTestStartedMessage { LatencyTestMessageType Type; LatencyTestStarted TestStarted; }; bool DecodeLatencyTestStartedMessage(LatencyTestStartedMessage* message, UByte* buffer, int size) { memset(message, 0, sizeof(LatencyTestStartedMessage)); if (size < 8) { message->Type = LatencyTestMessage_SizeError; return false; } switch (buffer[0]) { case LatencyTestMessage_TestStarted: message->Type = message->TestStarted.Decode(buffer, size); break; default: message->Type = LatencyTestMessage_Unknown; break; } return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); } struct LatencyTestButton { UInt16 CommandID; UInt16 Timestamp; LatencyTestMessageType Decode(const UByte* buffer, int size) { if (size < 5) return LatencyTestMessage_SizeError; CommandID = DecodeUInt16(buffer + 1); Timestamp = DecodeUInt16(buffer + 3); return LatencyTestMessage_Button; } }; struct LatencyTestButtonMessage { LatencyTestMessageType Type; LatencyTestButton Button; }; bool DecodeLatencyTestButtonMessage(LatencyTestButtonMessage* message, UByte* buffer, int size) { memset(message, 0, sizeof(LatencyTestButtonMessage)); if (size < 5) { message->Type = LatencyTestMessage_SizeError; return false; } switch (buffer[0]) { case LatencyTestMessage_Button: message->Type = message->Button.Decode(buffer, size); break; default: message->Type = LatencyTestMessage_Unknown; break; } return (message->Type < LatencyTestMessage_Unknown) && (message->Type != LatencyTestMessage_None); } struct LatencyTestConfigurationImpl { enum { PacketSize = 5 }; UByte Buffer[PacketSize]; OVR::LatencyTestConfiguration Configuration; LatencyTestConfigurationImpl(const OVR::LatencyTestConfiguration& configuration) : Configuration(configuration) { Pack(); } void Pack() { Buffer[0] = 5; Buffer[1] = UByte(Configuration.SendSamples); Buffer[2] = Configuration.Threshold.R; Buffer[3] = Configuration.Threshold.G; Buffer[4] = Configuration.Threshold.B; } void Unpack() { Configuration.SendSamples = Buffer[1] != 0 ? true : false; Configuration.Threshold.R = Buffer[2]; Configuration.Threshold.G = Buffer[3]; Configuration.Threshold.B = Buffer[4]; } }; struct LatencyTestCalibrateImpl { enum { PacketSize = 4 }; UByte Buffer[PacketSize]; Color CalibrationColor; LatencyTestCalibrateImpl(const Color& calibrationColor) : CalibrationColor(calibrationColor) { Pack(); } void Pack() { Buffer[0] = 7; Buffer[1] = CalibrationColor.R; Buffer[2] = CalibrationColor.G; Buffer[3] = CalibrationColor.B; } void Unpack() { CalibrationColor.R = Buffer[1]; CalibrationColor.G = Buffer[2]; CalibrationColor.B = Buffer[3]; } }; struct LatencyTestStartTestImpl { enum { PacketSize = 6 }; UByte Buffer[PacketSize]; Color TargetColor; LatencyTestStartTestImpl(const Color& targetColor) : TargetColor(targetColor) { Pack(); } void Pack() { UInt16 commandID = 1; Buffer[0] = 8; EncodeUInt16(Buffer+1, commandID); Buffer[3] = TargetColor.R; Buffer[4] = TargetColor.G; Buffer[5] = TargetColor.B; } void Unpack() { // UInt16 commandID = DecodeUInt16(Buffer+1); TargetColor.R = Buffer[3]; TargetColor.G = Buffer[4]; TargetColor.B = Buffer[5]; } }; struct LatencyTestDisplayImpl { enum { PacketSize = 6 }; UByte Buffer[PacketSize]; OVR::LatencyTestDisplay Display; LatencyTestDisplayImpl(const OVR::LatencyTestDisplay& display) : Display(display) { Pack(); } void Pack() { Buffer[0] = 9; Buffer[1] = Display.Mode; EncodeUInt32(Buffer+2, Display.Value); } void Unpack() { Display.Mode = Buffer[1]; Display.Value = DecodeUInt32(Buffer+2); } }; //------------------------------------------------------------------------------------- // ***** LatencyTestDeviceFactory LatencyTestDeviceFactory &LatencyTestDeviceFactory::GetInstance() { static LatencyTestDeviceFactory instance; return instance; } void LatencyTestDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) { class LatencyTestEnumerator : public HIDEnumerateVisitor { // Assign not supported; suppress MSVC warning. void operator = (const LatencyTestEnumerator&) { } DeviceFactory* pFactory; EnumerateVisitor& ExternalVisitor; public: LatencyTestEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor) : pFactory(factory), ExternalVisitor(externalVisitor) { } virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) { return pFactory->MatchVendorProduct(vendorId, productId); } virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc) { OVR_UNUSED(device); LatencyTestDeviceCreateDesc createDesc(pFactory, desc); ExternalVisitor.Visit(createDesc); } }; LatencyTestEnumerator latencyTestEnumerator(this, visitor); GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&latencyTestEnumerator); } bool LatencyTestDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const { return ((vendorId == LatencyTester_VendorId) && (productId == LatencyTester_ProductId)); } bool LatencyTestDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) { if (MatchVendorProduct(desc.VendorId, desc.ProductId)) { LatencyTestDeviceCreateDesc createDesc(this, desc); return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL; } return false; } //------------------------------------------------------------------------------------- // ***** LatencyTestDeviceCreateDesc DeviceBase* LatencyTestDeviceCreateDesc::NewDeviceInstance() { return new LatencyTestDeviceImpl(this); } bool LatencyTestDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const { if ((info->InfoClassType != Device_LatencyTester) && (info->InfoClassType != Device_None)) return false; info->Type = Device_LatencyTester; info->ProductName = HIDDesc.Product; info->Manufacturer = HIDDesc.Manufacturer; info->Version = HIDDesc.VersionNumber; if (info->InfoClassType == Device_LatencyTester) { SensorInfo* sinfo = (SensorInfo*)info; sinfo->VendorId = HIDDesc.VendorId; sinfo->ProductId = HIDDesc.ProductId; sinfo->SerialNumber = HIDDesc.SerialNumber; } return true; } //------------------------------------------------------------------------------------- // ***** LatencyTestDevice LatencyTestDeviceImpl::LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc) : OVR::HIDDeviceImpl(createDesc, 0) { } LatencyTestDeviceImpl::~LatencyTestDeviceImpl() { // Check that Shutdown() was called. OVR_ASSERT(!pCreateDesc->pDevice); } // Internal creation APIs. bool LatencyTestDeviceImpl::Initialize(DeviceBase* parent) { if (HIDDeviceImpl::Initialize(parent)) { LogText("OVR::LatencyTestDevice initialized.\n"); return true; } return false; } void LatencyTestDeviceImpl::Shutdown() { HIDDeviceImpl::Shutdown(); LogText("OVR::LatencyTestDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr()); } void LatencyTestDeviceImpl::OnInputReport(UByte* pData, UInt32 length) { bool processed = false; if (!processed) { LatencyTestSamplesMessage message; if (DecodeLatencyTestSamplesMessage(&message, pData, length)) { processed = true; onLatencyTestSamplesMessage(&message); } } if (!processed) { LatencyTestColorDetectedMessage message; if (DecodeLatencyTestColorDetectedMessage(&message, pData, length)) { processed = true; onLatencyTestColorDetectedMessage(&message); } } if (!processed) { LatencyTestStartedMessage message; if (DecodeLatencyTestStartedMessage(&message, pData, length)) { processed = true; onLatencyTestStartedMessage(&message); } } if (!processed) { LatencyTestButtonMessage message; if (DecodeLatencyTestButtonMessage(&message, pData, length)) { processed = true; onLatencyTestButtonMessage(&message); } } } bool LatencyTestDeviceImpl::SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag) { bool result = false; ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); if (GetManagerImpl()->GetThreadId() != OVR::GetCurrentThreadId()) { if (!waitFlag) { return queue->PushCall(this, &LatencyTestDeviceImpl::setConfiguration, configuration); } if (!queue->PushCallAndWaitResult( this, &LatencyTestDeviceImpl::setConfiguration, &result, configuration)) { return false; } } else return setConfiguration(configuration); return result; } bool LatencyTestDeviceImpl::setConfiguration(const OVR::LatencyTestConfiguration& configuration) { LatencyTestConfigurationImpl ltc(configuration); return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize); } bool LatencyTestDeviceImpl::GetConfiguration(OVR::LatencyTestConfiguration* configuration) { bool result = false; ThreadCommandQueue* pQueue = this->GetManagerImpl()->GetThreadQueue(); if (!pQueue->PushCallAndWaitResult(this, &LatencyTestDeviceImpl::getConfiguration, &result, configuration)) return false; return result; } bool LatencyTestDeviceImpl::getConfiguration(OVR::LatencyTestConfiguration* configuration) { LatencyTestConfigurationImpl ltc(*configuration); if (GetInternalDevice()->GetFeatureReport(ltc.Buffer, LatencyTestConfigurationImpl::PacketSize)) { ltc.Unpack(); *configuration = ltc.Configuration; return true; } return false; } bool LatencyTestDeviceImpl::SetCalibrate(const Color& calibrationColor, bool waitFlag) { bool result = false; ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); if (!waitFlag) { return queue->PushCall(this, &LatencyTestDeviceImpl::setCalibrate, calibrationColor); } if (!queue->PushCallAndWaitResult( this, &LatencyTestDeviceImpl::setCalibrate, &result, calibrationColor)) { return false; } return result; } bool LatencyTestDeviceImpl::setCalibrate(const Color& calibrationColor) { LatencyTestCalibrateImpl ltc(calibrationColor); return GetInternalDevice()->SetFeatureReport(ltc.Buffer, LatencyTestCalibrateImpl::PacketSize); } bool LatencyTestDeviceImpl::SetStartTest(const Color& targetColor, bool waitFlag) { bool result = false; ThreadCommandQueue* queue = GetManagerImpl()->GetThreadQueue(); if (!waitFlag) { return queue->PushCall(this, &LatencyTestDeviceImpl::setStartTest, targetColor); } if (!queue->PushCallAndWaitResult( this, &LatencyTestDeviceImpl::setStartTest, &result, targetColor)) { return false; } return result; } bool LatencyTestDeviceImpl::setStartTest(const Color& targetColor) { LatencyTestStartTestImpl ltst(targetColor); return GetInternalDevice()->SetFeatureReport(ltst.Buffer, LatencyTestStartTestImpl::PacketSize); } bool LatencyTestDeviceImpl::SetDisplay(const OVR::LatencyTestDisplay& display, bool waitFlag) { bool result = false; ThreadCommandQueue * queue = GetManagerImpl()->GetThreadQueue(); if (!waitFlag) { return queue->PushCall(this, &LatencyTestDeviceImpl::setDisplay, display); } if (!queue->PushCallAndWaitResult( this, &LatencyTestDeviceImpl::setDisplay, &result, display)) { return false; } return result; } bool LatencyTestDeviceImpl::setDisplay(const OVR::LatencyTestDisplay& display) { LatencyTestDisplayImpl ltd(display); return GetInternalDevice()->SetFeatureReport(ltd.Buffer, LatencyTestDisplayImpl::PacketSize); } void LatencyTestDeviceImpl::onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message) { if (message->Type != LatencyTestMessage_Samples) return; LatencyTestSamples& s = message->Samples; // Call OnMessage() within a lock to avoid conflicts with handlers. Lock::Locker scopeLock(HandlerRef.GetLock()); if (HandlerRef.HasHandlers()) { MessageLatencyTestSamples samples(this); for (UByte i = 0; i < s.SampleCount; i++) { samples.Samples.PushBack(Color(s.Samples[i].Value[0], s.Samples[i].Value[1], s.Samples[i].Value[2])); } HandlerRef.Call(samples); } } void LatencyTestDeviceImpl::onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message) { if (message->Type != LatencyTestMessage_ColorDetected) return; LatencyTestColorDetected& s = message->ColorDetected; // Call OnMessage() within a lock to avoid conflicts with handlers. Lock::Locker scopeLock(HandlerRef.GetLock()); if (HandlerRef.HasHandlers()) { MessageLatencyTestColorDetected detected(this); detected.Elapsed = s.Elapsed; detected.DetectedValue = Color(s.TriggerValue[0], s.TriggerValue[1], s.TriggerValue[2]); detected.TargetValue = Color(s.TargetValue[0], s.TargetValue[1], s.TargetValue[2]); HandlerRef.Call(detected); } } void LatencyTestDeviceImpl::onLatencyTestStartedMessage(LatencyTestStartedMessage* message) { if (message->Type != LatencyTestMessage_TestStarted) return; LatencyTestStarted& ts = message->TestStarted; // Call OnMessage() within a lock to avoid conflicts with handlers. Lock::Locker scopeLock(HandlerRef.GetLock()); if (HandlerRef.HasHandlers()) { MessageLatencyTestStarted started(this); started.TargetValue = Color(ts.TargetValue[0], ts.TargetValue[1], ts.TargetValue[2]); HandlerRef.Call(started); } } void LatencyTestDeviceImpl::onLatencyTestButtonMessage(LatencyTestButtonMessage* message) { if (message->Type != LatencyTestMessage_Button) return; // LatencyTestButton& s = message->Button; // Call OnMessage() within a lock to avoid conflicts with handlers. Lock::Locker scopeLock(HandlerRef.GetLock()); if (HandlerRef.HasHandlers()) { MessageLatencyTestButton button(this); HandlerRef.Call(button); } } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_LatencyTestImpl.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_LatencyTestImpl.h new file mode 100644 index 0000000..657ac0c --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_LatencyTestImpl.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_LatencyTestImpl.h Content : Latency Tester specific implementation. Created : March 7, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_LatencyTestImpl_h #define OVR_LatencyTestImpl_h #include "OVR_HIDDeviceImpl.h" namespace OVR { struct LatencyTestSamplesMessage; struct LatencyTestButtonMessage; struct LatencyTestStartedMessage; struct LatencyTestColorDetectedMessage; //------------------------------------------------------------------------------------- // LatencyTestDeviceFactory enumerates Oculus Latency Tester devices. class LatencyTestDeviceFactory : public DeviceFactory { public: static LatencyTestDeviceFactory &GetInstance(); // Enumerates devices, creating and destroying relevant objects in manager. virtual void EnumerateDevices(EnumerateVisitor& visitor); virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const; virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc); protected: DeviceManager* getManager() const { return (DeviceManager*) pManager; } }; // Describes a single a Oculus Latency Tester device and supports creating its instance. class LatencyTestDeviceCreateDesc : public HIDDeviceCreateDesc { public: LatencyTestDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) : HIDDeviceCreateDesc(factory, Device_LatencyTester, hidDesc) { } virtual DeviceCreateDesc* Clone() const { return new LatencyTestDeviceCreateDesc(*this); } virtual DeviceBase* NewDeviceInstance(); virtual MatchResult MatchDevice(const DeviceCreateDesc& other, DeviceCreateDesc**) const { if ((other.Type == Device_LatencyTester) && (pFactory == other.pFactory)) { const LatencyTestDeviceCreateDesc& s2 = (const LatencyTestDeviceCreateDesc&) other; if (MatchHIDDevice(s2.HIDDesc)) return Match_Found; } return Match_None; } virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const { // should paths comparison be case insensitive? return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && (HIDDesc.SerialNumber == hidDesc.SerialNumber)); } virtual bool GetDeviceInfo(DeviceInfo* info) const; }; //------------------------------------------------------------------------------------- // ***** OVR::LatencyTestDeviceImpl // Oculus Latency Tester interface. class LatencyTestDeviceImpl : public HIDDeviceImpl { public: LatencyTestDeviceImpl(LatencyTestDeviceCreateDesc* createDesc); ~LatencyTestDeviceImpl(); // DeviceCommon interface. virtual bool Initialize(DeviceBase* parent); virtual void Shutdown(); // DeviceManagerThread::Notifier interface. virtual void OnInputReport(UByte* pData, UInt32 length); // LatencyTesterDevice interface virtual bool SetConfiguration(const OVR::LatencyTestConfiguration& configuration, bool waitFlag = false); virtual bool GetConfiguration(OVR::LatencyTestConfiguration* configuration); virtual bool SetCalibrate(const Color& calibrationColor, bool waitFlag = false); virtual bool SetStartTest(const Color& targetColor, bool waitFlag = false); virtual bool SetDisplay(const LatencyTestDisplay& display, bool waitFlag = false); protected: bool openDevice(const char** errorFormatString); void closeDevice(); void closeDeviceOnIOError(); bool initializeRead(); bool processReadResult(); bool setConfiguration(const OVR::LatencyTestConfiguration& configuration); bool getConfiguration(OVR::LatencyTestConfiguration* configuration); bool setCalibrate(const Color& calibrationColor); bool setStartTest(const Color& targetColor); bool setDisplay(const OVR::LatencyTestDisplay& display); // Called for decoded messages void onLatencyTestSamplesMessage(LatencyTestSamplesMessage* message); void onLatencyTestButtonMessage(LatencyTestButtonMessage* message); void onLatencyTestStartedMessage(LatencyTestStartedMessage* message); void onLatencyTestColorDetectedMessage(LatencyTestColorDetectedMessage* message); }; } // namespace OVR #endif // OVR_LatencyTestImpl_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_DeviceManager.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_DeviceManager.cpp new file mode 100644 index 0000000..157a1ab --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_DeviceManager.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_DeviceManager.cpp Content : OSX specific DeviceManager implementation. Created : March 14, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_OSX_DeviceManager.h" // Sensor & HMD Factories #include "OVR_LatencyTestImpl.h" #include "OVR_SensorImpl.h" #include "OVR_OSX_HMDDevice.h" #include "OVR_OSX_HIDDevice.h" #include "Kernel/OVR_Timer.h" #include "Kernel/OVR_Std.h" #include "Kernel/OVR_Log.h" #include #include namespace OVR { namespace OSX { //------------------------------------------------------------------------------------- // **** OSX::DeviceManager DeviceManager::DeviceManager() { } DeviceManager::~DeviceManager() { OVR_DEBUG_LOG(("OSX::DeviceManager::~DeviceManager was called")); } bool DeviceManager::Initialize(DeviceBase*) { if (!DeviceManagerImpl::Initialize(0)) return false; // Start the background thread. pThread = *new DeviceManagerThread(); if (!pThread || !pThread->Start()) return false; // Wait for the thread to be fully up and running. pThread->StartupEvent.Wait(); // Do this now that we know the thread's run loop. HidDeviceManager = *HIDDeviceManager::CreateInternal(this); CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallBack, this); pCreateDesc->pDevice = this; LogText("OVR::DeviceManager - initialized.\n"); return true; } void DeviceManager::Shutdown() { LogText("OVR::DeviceManager - shutting down.\n"); CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallBack, this); // Set Manager shutdown marker variable; this prevents // any existing DeviceHandle objects from accessing device. pCreateDesc->pLock->pManager = 0; // Push for thread shutdown *WITH NO WAIT*. // This will have the following effect: // - Exit command will get enqueued, which will be executed later on the thread itself. // - Beyond this point, this DeviceManager object may be deleted by our caller. // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued // after pManager is null. // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last // reference to the thread object. pThread->Shutdown(); pThread.Clear(); DeviceManagerImpl::Shutdown(); } ThreadCommandQueue* DeviceManager::GetThreadQueue() { return pThread; } ThreadId DeviceManager::GetThreadId() const { return pThread->GetThreadId(); } bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const { if ((info->InfoClassType != Device_Manager) && (info->InfoClassType != Device_None)) return false; info->Type = Device_Manager; info->Version = 0; info->ProductName = "DeviceManager"; info->Manufacturer = "Oculus VR, Inc."; return true; } DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) { // TBD: Can this be avoided in the future, once proper device notification is in place? pThread->PushCall((DeviceManagerImpl*)this, &DeviceManager::EnumerateAllFactoryDevices, true); return DeviceManagerImpl::EnumerateDevicesEx(args); } void DeviceManager::displayReconfigurationCallBack (CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo) { DeviceManager* manager = reinterpret_cast(userInfo); OVR_UNUSED(manager); if (flags & kCGDisplayAddFlag) { LogText("Display Added, id = %d\n", int(display)); manager->EnumerateDevices(); } else if (flags & kCGDisplayRemoveFlag) { LogText("Display Removed, id = %d\n", int(display)); manager->EnumerateDevices(); } } //------------------------------------------------------------------------------------- // ***** DeviceManager Thread DeviceManagerThread::DeviceManagerThread() : Thread(ThreadStackSize) { } DeviceManagerThread::~DeviceManagerThread() { } int DeviceManagerThread::Run() { SetThreadName("OVR::DeviceManagerThread"); LogText("OVR::DeviceManagerThread - running (ThreadId=0x%p).\n", GetThreadId()); // Store out the run loop ref. RunLoop = CFRunLoopGetCurrent(); // Create a 'source' to enable us to signal the run loop to process the command queue. CFRunLoopSourceContext sourceContext; memset(&sourceContext, 0, sizeof(sourceContext)); sourceContext.version = 0; sourceContext.info = this; sourceContext.perform = &staticCommandQueueSourceCallback; CommandQueueSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 , &sourceContext); CFRunLoopAddSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); // Signal to the parent thread that initialization has finished. StartupEvent.SetEvent(); ThreadCommand::PopBuffer command; while(!IsExiting()) { // PopCommand will reset event on empty queue. if (PopCommand(&command)) { command.Execute(); } else { SInt32 exitReason = 0; do { UInt32 waitMs = INT_MAX; // If devices have time-dependent logic registered, get the longest wait // allowed based on current ticks. if (!TicksNotifiers.IsEmpty()) { double timeSeconds = Timer::GetSeconds(); unsigned waitAllowed; for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) { waitAllowed = (unsigned)(TicksNotifiers[j]->OnTicks(timeSeconds) * Timer::MsPerSecond); if (waitAllowed < (unsigned)waitMs) waitMs = waitAllowed; } } // Enter blocking run loop. We may continue until we timeout in which // case it's time to service the ticks. Or if commands arrive in the command // queue then the source callback will call 'CFRunLoopStop' causing this // to return. CFTimeInterval blockInterval = 0.001 * (double) waitMs; exitReason = CFRunLoopRunInMode(kCFRunLoopDefaultMode, blockInterval, false); if (exitReason == kCFRunLoopRunFinished) { // Maybe this will occur during shutdown? break; } else if (exitReason == kCFRunLoopRunStopped ) { // Commands need processing or we're being shutdown. break; } else if (exitReason == kCFRunLoopRunTimedOut) { // Timed out so that we can service our ticks callbacks. continue; } else if (exitReason == kCFRunLoopRunHandledSource) { // Should never occur since the last param when we call // 'CFRunLoopRunInMode' is false. OVR_ASSERT(false); break; } else { OVR_ASSERT_LOG(false, ("CFRunLoopRunInMode returned unexpected code")); break; } } while(true); } } CFRunLoopRemoveSource(RunLoop, CommandQueueSource, kCFRunLoopDefaultMode); CFRelease(CommandQueueSource); LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%p).\n", GetThreadId()); return 0; } void DeviceManagerThread::staticCommandQueueSourceCallback(void* pContext) { DeviceManagerThread* pThread = (DeviceManagerThread*) pContext; pThread->commandQueueSourceCallback(); } void DeviceManagerThread::commandQueueSourceCallback() { CFRunLoopStop(RunLoop); } bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) { TicksNotifiers.PushBack(notify); return true; } bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) { for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) { if (TicksNotifiers[i] == notify) { TicksNotifiers.RemoveAt(i); return true; } } return false; } void DeviceManagerThread::Shutdown() { // Push for thread shutdown *WITH NO WAIT*. // This will have the following effect: // - Exit command will get enqueued, which will be executed later on the thread itself. // - Beyond this point, this DeviceManager object may be deleted by our caller. // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued // after pManager is null. // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last // reference to the thread object. PushExitCommand(false); // make sure CFRunLoopRunInMode is woken up CFRunLoopSourceSignal(CommandQueueSource); CFRunLoopWakeUp(RunLoop); } } // namespace OSX //------------------------------------------------------------------------------------- // ***** Creation // Creates a new DeviceManager and initializes OVR. DeviceManager* DeviceManager::Create() { if (!System::IsInitialized()) { // Use custom message, since Log is not yet installed. OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); return 0; } Ptr manager = *new OSX::DeviceManager; if (manager) { if (manager->Initialize(0)) { manager->AddFactory(&LatencyTestDeviceFactory::GetInstance()); manager->AddFactory(&SensorDeviceFactory::GetInstance()); manager->AddFactory(&OSX::HMDDeviceFactory::GetInstance()); manager->AddRef(); } else { manager.Clear(); } } return manager.GetPtr(); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_DeviceManager.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_DeviceManager.h new file mode 100644 index 0000000..7913d3a --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_DeviceManager.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_DeviceManager.h Content : OSX specific DeviceManager header. Created : March 14, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_OSX_DeviceManager_h #define OVR_OSX_DeviceManager_h #include "OVR_DeviceImpl.h" #include "Kernel/OVR_Timer.h" #include #include namespace OVR { namespace OSX { class DeviceManagerThread; //------------------------------------------------------------------------------------- // ***** OSX DeviceManager class DeviceManager : public DeviceManagerImpl { public: DeviceManager(); ~DeviceManager(); // Initialize/Shutdown manager thread. virtual bool Initialize(DeviceBase* parent); virtual void Shutdown(); virtual ThreadCommandQueue* GetThreadQueue(); virtual ThreadId GetThreadId() const; virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); virtual bool GetDeviceInfo(DeviceInfo* info) const; protected: static void displayReconfigurationCallBack (CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo); public: // data Ptr pThread; }; //------------------------------------------------------------------------------------- // ***** Device Manager Background Thread class DeviceManagerThread : public Thread, public ThreadCommandQueue { friend class DeviceManager; enum { ThreadStackSize = 32 * 1024 }; public: DeviceManagerThread(); ~DeviceManagerThread(); virtual int Run(); // ThreadCommandQueue notifications for CommandEvent handling. virtual void OnPushNonEmpty_Locked() { CFRunLoopSourceSignal(CommandQueueSource); CFRunLoopWakeUp(RunLoop); } virtual void OnPopEmpty_Locked() {} // Notifier used for different updates (EVENT or regular timing or messages). class Notifier { public: // Called when timing ticks are updated. // Returns the largest number of microseconds // this function can wait till next call. virtual double OnTicks(double tickSeconds) { OVR_UNUSED1(tickSeconds); return 1000.0; } }; // Add notifier that will be called at regular intervals. bool AddTicksNotifier(Notifier* notify); bool RemoveTicksNotifier(Notifier* notify); CFRunLoopRef GetRunLoop() { return RunLoop; } void Shutdown(); private: CFRunLoopRef RunLoop; CFRunLoopSourceRef CommandQueueSource; static void staticCommandQueueSourceCallback(void* pContext); void commandQueueSourceCallback(); Event StartupEvent; // Ticks notifiers. Used for time-dependent events such as keep-alive. Array TicksNotifiers; }; }} // namespace OSX::OVR #endif // OVR_OSX_DeviceManager_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HIDDevice.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HIDDevice.cpp new file mode 100644 index 0000000..eea2bda --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HIDDevice.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_HIDDevice.cpp Content : OSX HID device implementation. Created : February 26, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_OSX_HIDDevice.h" #include namespace OVR { namespace OSX { //------------------------------------------------------------------------------------- // **** OSX::DeviceManager HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager) { HIDManager = NULL; } HIDDeviceManager::~HIDDeviceManager() { } CFRunLoopRef HIDDeviceManager::getRunLoop() { if (DevManager != NULL) { return DevManager->pThread->GetRunLoop(); } return CFRunLoopGetCurrent(); } bool HIDDeviceManager::initializeManager() { if (HIDManager != NULL) { return true; } HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); if (!HIDManager) { return false; } // Create a Matching Dictionary CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // Specify a device manufacturer in the Matching Dictionary UInt32 vendorId = Oculus_VendorId; CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId); CFDictionarySetValue(matchDict, CFSTR(kIOHIDVendorIDKey), vendorIdRef); // Register the Matching Dictionary to the HID Manager IOHIDManagerSetDeviceMatching(HIDManager, matchDict); CFRelease(vendorIdRef); CFRelease(matchDict); // Register a callback for USB device detection with the HID Manager IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this); IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode); return true; } bool HIDDeviceManager::Initialize() { return initializeManager(); } void HIDDeviceManager::Shutdown() { OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'.")); CFRelease(HIDManager); LogText("OVR::OSX::HIDDeviceManager - shutting down.\n"); } bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult) { CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName); if (!ref) { return false; } if (CFGetTypeID(ref) != CFNumberGetTypeID()) { return false; } CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult); return true; } bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) { if (!getVendorId(device, &(pDevDesc->VendorId))) { return false; } if (!getProductId(device, &(pDevDesc->ProductId))) { return false; } SInt32 result; if (!getIntProperty(device, CFSTR(kIOHIDVersionNumberKey), &result)) { return false; } pDevDesc->VersionNumber = result; return true; } bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) { SInt32 result; if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result)) { return false; } pDevDesc->UsagePage = result; if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result)) { return false; } pDevDesc->Usage = result; return true; } bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) { return getSerialNumberString(device, &(pDevDesc->SerialNumber)); } bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) { // Regardless of whether they fail we'll try and get the remaining. getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer)); getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product)); return true; } bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult) { CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName); if (!str) { return false; } CFIndex length = CFStringGetLength(str); CFRange range = CFRangeMake(0, length); // Test the conversion first to get required buffer size. CFIndex bufferLength; CFIndex numberOfChars = CFStringGetBytes(str, range, kCFStringEncodingUTF8, (char) '?', FALSE, NULL, 0, &bufferLength); if (numberOfChars == 0) { return false; } // Now allocate buffer. char* buffer = new char[bufferLength+1]; numberOfChars = CFStringGetBytes(str, range, kCFStringEncodingUTF8, (char) '?', FALSE, (UInt8*) buffer, bufferLength, NULL); OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed.")); buffer[bufferLength] = '\0'; *pResult = String(buffer); return true; } bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult) { SInt32 result; if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result)) { return false; } *pResult = result; return true; } bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult) { SInt32 result; if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result)) { return false; } *pResult = result; return true; } bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult) { SInt32 result; if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result)) { return false; } *pResult = result; return true; } bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult) { if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult)) { return false; } return true; } bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath) { String transport; if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport)) { return false; } UInt16 vendorId; if (!getVendorId(device, &vendorId)) { return false; } UInt16 productId; if (!getProductId(device, &productId)) { return false; } String serialNumber; if (!getSerialNumberString(device, &serialNumber)) { return false; } StringBuffer buffer; buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s", transport.ToCStr(), vendorId, productId, serialNumber.ToCStr()); *pPath = String(buffer); return true; } bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) { if (!initializeManager()) { return false; } CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager); if (!deviceSet) return false; CFIndex deviceCount = CFSetGetCount(deviceSet); // Allocate a block of memory and read the set into it. IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); CFSetGetValues(deviceSet, (const void **) devices); // Iterate over devices. for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) { IOHIDDeviceRef hidDev = devices[deviceIndex]; if (!hidDev) { continue; } HIDDeviceDesc devDesc; if (getPath(hidDev, &(devDesc.Path)) && initVendorProductVersion(hidDev, &devDesc) && enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) && initUsage(hidDev, &devDesc)) { initStrings(hidDev, &devDesc); initSerialNumber(hidDev, &devDesc); // Look for the device to check if it is already opened. Ptr existingDevice = DevManager->FindHIDDevice(devDesc, true); // if device exists and it is opened then most likely the CreateHIDFile // will fail; therefore, we just set Enumerated to 'true' and continue. if (existingDevice && existingDevice->pDevice) { existingDevice->Enumerated = true; continue; } // Construct minimal device that the visitor callback can get feature reports from. OSX::HIDDevice device(this, hidDev); enumVisitor->Visit(device, devDesc); } } OVR_FREE(devices); CFRelease(deviceSet); return true; } OVR::HIDDevice* HIDDeviceManager::Open(const String& path) { Ptr device = *new OSX::HIDDevice(this); if (!device->HIDInitialize(path)) { return NULL; } device->AddRef(); return device; } bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc) { if (!initVendorProductVersion(device, desc)) { return false; } if (!initUsage(device, desc)) { return false; } if (!initSerialNumber(device, desc)) { return false; } initStrings(device, desc); return true; } // New USB device specified in the matching dictionary has been added (callback function) void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) { OVR_UNUSED(inResult); OVR_UNUSED(inSender); HIDDeviceManager* hidMgr = static_cast(inContext); HIDDeviceDesc hidDevDesc; hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path); hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc); hidMgr->DevManager->DetectHIDDevice(hidDevDesc); } //------------------------------------------------------------------------------------- // **** OSX::HIDDevice HIDDevice::HIDDevice(HIDDeviceManager* manager) : InMinimalMode(false), HIDManager(manager) { Device = NULL; RepluggedNotificationPort = 0; } // This is a minimal constructor used during enumeration for us to pass // a HIDDevice to the visit function (so that it can query feature reports). HIDDevice::HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device) : InMinimalMode(true), HIDManager(manager), Device(device) { RepluggedNotificationPort = 0; } HIDDevice::~HIDDevice() { if (!InMinimalMode) { HIDShutdown(); } } bool HIDDevice::HIDInitialize(const String& path) { DevDesc.Path = path; if (!openDevice()) { LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr()); return false; } // Setup notification for when a device is unplugged and plugged back in. if (!setupDevicePluggedInNotification()) { LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in."); closeDevice(false); return false; } HIDManager->DevManager->pThread->AddTicksNotifier(this); LogText("OVR::OSX::HIDDevice - Opened '%s'\n" " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", DevDesc.Path.ToCStr(), DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), DevDesc.SerialNumber.ToCStr()); return true; } bool HIDDevice::initInfo() { // Device must have been successfully opened. OVR_ASSERT(Device); // Get report lengths. SInt32 bufferLength; bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength); OVR_ASSERT(getResult); InputReportBufferLength = (UInt16) bufferLength; getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength); OVR_ASSERT(getResult); OutputReportBufferLength = (UInt16) bufferLength; getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength); OVR_ASSERT(getResult); FeatureReportBufferLength = (UInt16) bufferLength; if (ReadBufferSize < InputReportBufferLength) { OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); return false; } // Get device desc. if (!HIDManager->getFullDesc(Device, &DevDesc)) { OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); return false; } return true; } void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator) { HIDDevice* pDevice = (HIDDevice*) pContext; pDevice->deviceAddedCallback(iterator); } void HIDDevice::deviceAddedCallback(io_iterator_t iterator) { if (Device == NULL) { if (openDevice()) { LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr()); Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc, true); if (existingHIDDev && existingHIDDev->pDevice) { HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev); } } } // Reset callback. while (IOIteratorNext(iterator)) ; } bool HIDDevice::openDevice() { // Have to iterate through devices again to generate paths. CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager); CFIndex deviceCount = CFSetGetCount(deviceSet); // Allocate a block of memory and read the set into it. IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); CFSetGetValues(deviceSet, (const void **) devices); // Iterate over devices. IOHIDDeviceRef device = NULL; for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) { IOHIDDeviceRef tmpDevice = devices[deviceIndex]; if (!tmpDevice) { continue; } String path; if (!HIDManager->getPath(tmpDevice, &path)) { continue; } if (path == DevDesc.Path) { device = tmpDevice; break; } } OVR_FREE(devices); if (!device) { CFRelease(deviceSet); return false; } // Attempt to open device. if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice) != kIOReturnSuccess) { CFRelease(deviceSet); return false; } // Retain the device before we release the set. CFRetain(device); CFRelease(deviceSet); Device = device; if (!initInfo()) { IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice); CFRelease(Device); Device = NULL; return false; } // Setup the Run Loop and callbacks. IOHIDDeviceScheduleWithRunLoop(Device, HIDManager->getRunLoop(), kCFRunLoopDefaultMode); IOHIDDeviceRegisterInputReportCallback(Device, ReadBuffer, ReadBufferSize, staticHIDReportCallback, this); IOHIDDeviceRegisterRemovalCallback(Device, staticDeviceRemovedCallback, this); return true; } void HIDDevice::HIDShutdown() { HIDManager->DevManager->pThread->RemoveTicksNotifier(this); if (Device != NULL) // Device may already have been closed if unplugged. { closeDevice(false); } IOObjectRelease(RepluggedNotification); if (RepluggedNotificationPort) IONotificationPortDestroy(RepluggedNotificationPort); LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); } bool HIDDevice::setupDevicePluggedInNotification() { // Setup notification when devices are plugged in. RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopSourceRef notificationRunLoopSource = IONotificationPortGetRunLoopSource(RepluggedNotificationPort); CFRunLoopAddSource(HIDManager->getRunLoop(), notificationRunLoopSource, kCFRunLoopDefaultMode); CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); // Have to specify vendorId and productId. Doesn't seem to accept additional // things like serial number. SInt32 vendorId = DevDesc.VendorId; CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendorId); CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef); CFRelease(numberRef); SInt32 deviceProductId = DevDesc.ProductId; numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &deviceProductId); CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef); CFRelease(numberRef); kern_return_t result = IOServiceAddMatchingNotification(RepluggedNotificationPort, kIOMatchedNotification, matchingDict, staticDeviceAddedCallback, this, &RepluggedNotification); if (result != KERN_SUCCESS) { CFRelease(RepluggedNotificationPort); RepluggedNotificationPort = 0; return false; } // Iterate through to arm. while (IOIteratorNext(RepluggedNotification)) { } return true; } void HIDDevice::closeDevice(bool wasUnplugged) { OVR_ASSERT(Device != NULL); if (!wasUnplugged) { // Clear the registered callbacks. IOHIDDeviceRegisterInputReportCallback(Device, ReadBuffer, InputReportBufferLength, NULL, this); IOHIDDeviceRegisterRemovalCallback(Device, NULL, this); IOHIDDeviceUnscheduleFromRunLoop(Device, HIDManager->getRunLoop(), kCFRunLoopDefaultMode); IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone); } CFRelease(Device); Device = NULL; LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); } void HIDDevice::staticHIDReportCallback(void* pContext, IOReturn result, void* pSender, IOHIDReportType reportType, uint32_t reportId, uint8_t* pReport, CFIndex reportLength) { OVR_UNUSED(result); OVR_UNUSED(pSender); OVR_UNUSED(reportType); OVR_UNUSED(reportId); HIDDevice* pDevice = (HIDDevice*) pContext; return pDevice->hidReportCallback(pReport, (UInt32)reportLength); } void HIDDevice::hidReportCallback(UByte* pData, UInt32 length) { // We got data. if (Handler) { Handler->OnInputReport(pData, length); } } void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender) { OVR_UNUSED(result); OVR_UNUSED(pSender); HIDDevice* pDevice = (HIDDevice*) pContext; pDevice->deviceRemovedCallback(); } void HIDDevice::deviceRemovedCallback() { Ptr _this(this); // prevent from release Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc, true); if (existingHIDDev && existingHIDDev->pDevice) { HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev); } closeDevice(true); } CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device) { const UInt32 safeBuffSize = 256; char nameBuff[safeBuffSize]; OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device); return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII); } bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) { if (!Device) return false; UByte reportID = data[0]; if (reportID == 0) { // Not using reports so remove from data packet. data++; length--; } IOReturn result = IOHIDDeviceSetReport( Device, kIOHIDReportTypeFeature, reportID, data, length); return (result == kIOReturnSuccess); } bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) { if (!Device) return false; CFIndex bufferLength = length; // Report id is in first byte of the buffer. IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength); return (result == kIOReturnSuccess); } double HIDDevice::OnTicks(double tickSeconds) { if (Handler) { return Handler->OnTicks(tickSeconds); } return DeviceManagerThread::Notifier::OnTicks(tickSeconds); } HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::DeviceManager* devManager) { if (!System::IsInitialized()) { // Use custom message, since Log is not yet installed. OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); return 0; } Ptr manager = *new OSX::HIDDeviceManager(devManager); if (manager) { if (manager->Initialize()) { manager->AddRef(); } else { manager.Clear(); } } return manager.GetPtr(); } } // namespace OSX //------------------------------------------------------------------------------------- // ***** Creation // Creates a new HIDDeviceManager and initializes OVR. HIDDeviceManager* HIDDeviceManager::Create(Ptr& deviceManager) { if (!System::IsInitialized()) { // Use custom message, since Log is not yet installed. OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); return 0; } Ptr deviceManagerOSX = *new OSX::DeviceManager; if (!deviceManagerOSX) { return NULL; } if (!deviceManagerOSX->Initialize(NULL)) { return NULL; } deviceManager = deviceManagerOSX; return deviceManagerOSX->GetHIDDeviceManager(); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HIDDevice.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HIDDevice.h new file mode 100644 index 0000000..ca340e4 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HIDDevice.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_HIDDevice.h Content : OSX HID device implementation. Created : February 26, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_OSX_HIDDevice_h #define OVR_OSX_HIDDevice_h #include "OVR_HIDDevice.h" #include "OVR_OSX_DeviceManager.h" #include namespace OVR { namespace OSX { class HIDDeviceManager; //------------------------------------------------------------------------------------- // ***** OSX HIDDevice class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier { private: friend class HIDDeviceManager; public: HIDDevice(HIDDeviceManager* manager); // This is a minimal constructor used during enumeration for us to pass // a HIDDevice to the visit function (so that it can query feature reports). HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device); virtual ~HIDDevice(); bool HIDInitialize(const String& path); void HIDShutdown(); virtual bool SetFeatureReport(UByte* data, UInt32 length); virtual bool GetFeatureReport(UByte* data, UInt32 length); bool Write(UByte* data, UInt32 length); bool Read(UByte* pData, UInt32 length, UInt32 timeoutMilliS); bool ReadBlocking(UByte* pData, UInt32 length); // DeviceManagerThread::Notifier double OnTicks(double tickSeconds); private: bool initInfo(); bool openDevice(); void closeDevice(bool wasUnplugged); bool setupDevicePluggedInNotification(); CFStringRef generateRunLoopModeString(IOHIDDeviceRef device); static void staticHIDReportCallback(void* pContext, IOReturn result, void* pSender, IOHIDReportType reportType, uint32_t reportId, uint8_t* pReport, CFIndex reportLength); void hidReportCallback(UByte* pData, UInt32 length); static void staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender); void deviceRemovedCallback(); static void staticDeviceAddedCallback(void* pContext, io_iterator_t iterator); void deviceAddedCallback(io_iterator_t iterator); bool InMinimalMode; HIDDeviceManager* HIDManager; IOHIDDeviceRef Device; HIDDeviceDesc DevDesc; enum { ReadBufferSize = 96 }; UByte ReadBuffer[ReadBufferSize]; UInt16 InputReportBufferLength; UInt16 OutputReportBufferLength; UInt16 FeatureReportBufferLength; IONotificationPortRef RepluggedNotificationPort; io_iterator_t RepluggedNotification; }; //------------------------------------------------------------------------------------- // ***** OSX HIDDeviceManager class HIDDeviceManager : public OVR::HIDDeviceManager { friend class HIDDevice; public: HIDDeviceManager(OSX::DeviceManager* Manager); virtual ~HIDDeviceManager(); virtual bool Initialize(); virtual void Shutdown(); virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); virtual OVR::HIDDevice* Open(const String& path); static HIDDeviceManager* CreateInternal(DeviceManager* manager); private: CFRunLoopRef getRunLoop(); bool initializeManager(); bool initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); bool initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); bool initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); bool initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc); bool getVendorId(IOHIDDeviceRef device, UInt16* pResult); bool getProductId(IOHIDDeviceRef device, UInt16* pResult); bool getLocationId(IOHIDDeviceRef device, SInt32* pResult); bool getSerialNumberString(IOHIDDeviceRef device, String* pResult); bool getPath(IOHIDDeviceRef device, String* pPath); bool getIntProperty(IOHIDDeviceRef device, CFStringRef key, int32_t* pResult); bool getStringProperty(IOHIDDeviceRef device, CFStringRef propertyName, String* pResult); bool getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc); static void staticDeviceMatchingCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef); DeviceManager* DevManager; IOHIDManagerRef HIDManager; }; }} // namespace OVR::OSX #endif // OVR_OSX_HIDDevice_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HMDDevice.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HMDDevice.cpp new file mode 100644 index 0000000..97b1263 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HMDDevice.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_HMDDevice.cpp Content : OSX Interface to HMD - detects HMD display Created : September 21, 2012 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_OSX_HMDDevice.h" #include "OVR_OSX_DeviceManager.h" #include "Util/Util_Render_Stereo.h" #include "OVR_OSX_HMDDevice.h" #include #include #include #include namespace OVR { namespace OSX { using namespace OVR::Util::Render; //------------------------------------------------------------------------------------- HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, UInt32 vend, UInt32 prod, const String& displayDeviceName, int dispId) : DeviceCreateDesc(factory, Device_HMD), DisplayDeviceName(displayDeviceName), Contents(0), DisplayId(dispId) { OVR_UNUSED(vend); OVR_UNUSED(prod); DeviceId = DisplayDeviceName; Desktop.X = 0; Desktop.Y = 0; ResolutionInPixels = Sizei(0); ScreenSizeInMeters = Sizef(0.0f); VCenterFromTopInMeters = 0.0f; LensSeparationInMeters = 0.0f; } HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) : DeviceCreateDesc(other.pFactory, Device_HMD), DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), Contents(other.Contents), DisplayId(other.DisplayId) { Desktop.X = other.Desktop.X; Desktop.Y = other.Desktop.Y; ResolutionInPixels = other.ResolutionInPixels; ScreenSizeInMeters = other.ScreenSizeInMeters; VCenterFromTopInMeters = other.VCenterFromTopInMeters; LensSeparationInMeters = other.LensSeparationInMeters; } HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, DeviceCreateDesc** pcandidate) const { if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) return Match_None; // There are several reasons we can come in here: // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc // - Require exact device DeviceId/DeviceName match // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc // - This DeviceId is empty; becomes candidate // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc // - This other.DeviceId is empty; becomes candidate const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; if ((DeviceId == s2.DeviceId) && (DisplayId == s2.DisplayId)) { // Non-null DeviceId may match while size is different if screen size was overwritten // by SensorDisplayInfo in prior iteration. if (!DeviceId.IsEmpty() || (ScreenSizeInMeters == s2.ScreenSizeInMeters) ) { *pcandidate = 0; return Match_Found; } } // DisplayInfo takes precedence, although we try to match it first. if ((ResolutionInPixels == s2.ResolutionInPixels) && (ScreenSizeInMeters == s2.ScreenSizeInMeters)) { if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) { *pcandidate = const_cast((const DeviceCreateDesc*)this); return Match_Candidate; } *pcandidate = 0; return Match_Found; } // SensorDisplayInfo may override resolution settings, so store as candidiate. if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) { *pcandidate = const_cast((const DeviceCreateDesc*)this); return Match_Candidate; } // OTHER HMD Monitor desc may initialize DeviceName/Id else if (DeviceId.IsEmpty() && DisplayId == 0) { *pcandidate = const_cast((const DeviceCreateDesc*)this); return Match_Candidate; } return Match_None; } bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, bool* newDeviceFlag) { // This candidate was the the "best fit" to apply sensor DisplayInfo to. OVR_ASSERT(other.Type == Device_HMD); const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; // Force screen size on resolution from SensorDisplayInfo. // We do this because USB detection is more reliable as compared to HDMI EDID, // which may be corrupted by splitter reporting wrong monitor if (s2.DeviceId.IsEmpty() && s2.DisplayId == 0) { ScreenSizeInMeters = s2.ScreenSizeInMeters; Contents |= Contents_Screen; if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) { memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); // TODO: DistortionEqn Contents |= Contents_Distortion; } DeviceId = s2.DeviceId; DisplayId = s2.DisplayId; DisplayDeviceName = s2.DisplayDeviceName; Desktop.X = s2.Desktop.X; Desktop.Y = s2.Desktop.Y; if (newDeviceFlag) *newDeviceFlag = true; } else if (DeviceId.IsEmpty()) { // This branch is executed when 'fake' HMD descriptor is being replaced by // the real one. DeviceId = s2.DeviceId; DisplayId = s2.DisplayId; DisplayDeviceName = s2.DisplayDeviceName; Desktop.X = s2.Desktop.X; Desktop.Y = s2.Desktop.Y; // ScreenSize and Resolution are NOT assigned here, since they may have // come from a sensor DisplayInfo (which has precedence over HDMI). if (newDeviceFlag) *newDeviceFlag = true; } else { if (newDeviceFlag) *newDeviceFlag = false; } return true; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // ***** HMDDeviceFactory HMDDeviceFactory &HMDDeviceFactory::GetInstance() { static HMDDeviceFactory instance; return instance; } void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) { CGDirectDisplayID Displays[32]; uint32_t NDisplays = 0; CGGetOnlineDisplayList(32, Displays, &NDisplays); for (unsigned int i = 0; i < NDisplays; i++) { io_service_t port = CGDisplayIOServicePort(Displays[i]); CFDictionaryRef DispInfo = IODisplayCreateInfoDictionary(port, kIODisplayMatchingInfo); uint32_t vendor = CGDisplayVendorNumber(Displays[i]); uint32_t product = CGDisplayModelNumber(Displays[i]); CGRect desktop = CGDisplayBounds(Displays[i]); if (vendor == 16082 && ( (product == 1)||(product == 2) ) ) // 7" or HD { char idstring[9]; idstring[0] = 'A'-1+((vendor>>10) & 31); idstring[1] = 'A'-1+((vendor>>5) & 31); idstring[2] = 'A'-1+((vendor>>0) & 31); snprintf(idstring+3, 5, "%04d", product); HMDDeviceCreateDesc hmdCreateDesc(this, vendor, product, idstring, Displays[i]); // Hard-coded defaults in case the device doesn't have the data itself. if (product == 3) { // DK2 prototypes and variants (default to HmdType_DK2) hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, 1920, 1080, 0.12576f, 0.07074f, 0.12576f*0.5f, 0.0635f ); } else if (product == 2) { // HD Prototypes (default to HmdType_DKHDProto) hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, 1920, 1080, 0.12096f, 0.06804f, 0.06804f*0.5f, 0.0635f); } else if (product == 1) { // DK1 hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, 1280, 800, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f); } else { // Future Oculus HMD devices (default to DK1 dimensions) hmdCreateDesc.SetScreenParameters(desktop.origin.x, desktop.origin.y, 1280, 800, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f); } OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %x:%x\n", vendor, product)); // Notify caller about detected device. This will call EnumerateAddDevice // if the this is the first time device was detected. visitor.Visit(hmdCreateDesc); } CFRelease(DispInfo); } } #include "OVR_Common_HMDDevice.cpp" }} // namespace OVR::OSX \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HMDDevice.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HMDDevice.h new file mode 100644 index 0000000..1751dea --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_HMDDevice.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_HMDDevice.h Content : OSX HMDDevice implementation Created : September 21, 2012 Authors : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_OSX_HMDDevice_h #define OVR_OSX_HMDDevice_h #include "OVR_DeviceImpl.h" #include "Kernel/OVR_String.h" #include "OVR_Profile.h" namespace OVR { namespace OSX { class HMDDevice; //------------------------------------------------------------------------------------- // HMDDeviceFactory enumerates attached Oculus HMD devices. // // This is currently done by matching monitor device strings. class HMDDeviceFactory : public DeviceFactory { public: static HMDDeviceFactory &GetInstance(); // Enumerates devices, creating and destroying relevant objects in manager. virtual void EnumerateDevices(EnumerateVisitor& visitor); protected: DeviceManager* getManager() const { return (DeviceManager*) pManager; } }; class HMDDeviceCreateDesc : public DeviceCreateDesc { friend class HMDDevice; protected: enum { Contents_Screen = 1, Contents_Distortion = 2, }; String DeviceId; String DisplayDeviceName; struct { int X, Y; } Desktop; unsigned int Contents; Sizei ResolutionInPixels; Sizef ScreenSizeInMeters; float VCenterFromTopInMeters; float LensSeparationInMeters; // TODO: update these to splines. DistortionEqnType DistortionEqn; float DistortionK[4]; int DisplayId; public: HMDDeviceCreateDesc(DeviceFactory* factory, UInt32 vendor, UInt32 product, const String& displayDeviceName, int dispId); HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); virtual DeviceCreateDesc* Clone() const { return new HMDDeviceCreateDesc(*this); } virtual DeviceBase* NewDeviceInstance(); virtual MatchResult MatchDevice(const DeviceCreateDesc& other, DeviceCreateDesc**) const; virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); virtual bool GetDeviceInfo(DeviceInfo* info) const; void SetScreenParameters(int x, int y, int hres, int vres, float hsize, float vsize, float vCenterFromTopInMeters, float lensSeparationInMeters); void SetDistortion(const float* dks); HmdTypeEnum GetHmdType() const; }; //------------------------------------------------------------------------------------- // 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 // first creating a Sensor object and then wrappig it in SensorFusion. class HMDDevice : public DeviceImpl { public: HMDDevice(HMDDeviceCreateDesc* createDesc); ~HMDDevice(); virtual bool Initialize(DeviceBase* parent); virtual void Shutdown(); // Requests the currently used default profile. This profile affects the // settings reported by HMDInfo. virtual Profile* GetProfile(); virtual const char* GetProfileName(); virtual bool SetProfileName(const char* name); // Query associated sensor. virtual OVR::SensorDevice* GetSensor(); protected: HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); } // User name for the profile used with this device. String ProfileName; mutable Ptr pCachedProfile; }; }} // namespace OVR::OSX #endif // OVR_OSX_HMDDevice_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_SensorDevice.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_SensorDevice.cpp new file mode 100644 index 0000000..afb8c22 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_OSX_SensorDevice.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_OSX_SensorDevice.cpp Content : OSX SensorDevice implementation Created : March 14, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_OSX_HMDDevice.h" #include "OVR_SensorImpl.h" #include "OVR_DeviceImpl.h" namespace OVR { namespace OSX { } // namespace OSX //------------------------------------------------------------------------------------- void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo, DeviceFactory::EnumerateVisitor& visitor) { OSX::HMDDeviceCreateDesc hmdCreateDesc(&OSX::HMDDeviceFactory::GetInstance(), 1, 1, "", 0); hmdCreateDesc.SetScreenParameters( 0, 0, displayInfo.HResolution, displayInfo.VResolution, displayInfo.HScreenSize, displayInfo.VScreenSize, displayInfo.VCenter, displayInfo.LensSeparation); if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) { hmdCreateDesc.SetDistortion(displayInfo.DistortionK); // TODO: add DistortionEqn } visitor.Visit(hmdCreateDesc); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Profile.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Profile.cpp new file mode 100644 index 0000000..fbaaa3b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Profile.cpp @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None 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 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. Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Profile.h" #include "OVR_Device.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" #ifdef OVR_OS_WIN32 #include #else #include #include #ifdef OVR_OS_LINUX #include #include #endif #endif #define PROFILE_VERSION 2.0 #define MAX_PROFILE_MAJOR_VERSION 2 #define MAX_DEVICE_PROFILE_MAJOR_VERSION 1 namespace OVR { //----------------------------------------------------------------------------- // Returns the pathname of the JSON file containing the stored profiles String GetBaseOVRPath(bool create_dir) { String path; #if defined(OVR_OS_WIN32) TCHAR data_path[MAX_PATH]; SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, NULL, 0, data_path); path = String(data_path); path += "/Oculus"; if (create_dir) { // Create the Oculus directory if it doesn't exist WCHAR wpath[128]; OVR::UTF8Util::DecodeString(wpath, path.ToCStr()); 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"); path = home; path += "/Library/Preferences/Oculus"; if (create_dir) { // Create the Oculus directory if it doesn't exist DIR* dir = opendir(path); if (dir == NULL) { mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); } else { closedir(dir); } } #else 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 DIR* dir = opendir(path); if (dir == NULL) { mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); } else { closedir(dir); } } #endif return path; } String ProfileManager::GetProfilePath(bool create_dir) { String path = GetBaseOVRPath(create_dir); path += "/ProfileDB.json"; return path; } bool ProfileManager::GetDeviceTags(const DeviceBase* device, String& product, String& serial) { product = ""; serial = ""; if (device && device->GetType() == Device_HMD) { HMDDevice* hmd = (HMDDevice*)device; Ptr sensor = *(hmd->GetSensor()); if (sensor) { SensorInfo sinfo; sensor->GetDeviceInfo(&sinfo); serial = sinfo.SerialNumber; // get the serial number // Derive the product tag from the HMD product name HMDInfo hmdinfo; hmd->GetDeviceInfo(&hmdinfo); const char* product_name = NULL; // If the HMD is unrecognized then use the name stamped into the // sensor firmware if (hmdinfo.HmdType == HmdType_None || hmdinfo.HmdType == HmdType_Unknown) product_name = sinfo.ProductName.ToCStr(); else product_name = hmdinfo.ProductName.ToCStr(); // First strip off "Oculus" const char* oculus = strstr(product_name, "Oculus "); if (oculus) product_name = oculus + OVR_strlen("Oculus "); // And remove spaces from the name for (const char* s=product_name; *s != 0; s++) { if (*s != ' ') product.AppendChar(*s); } } } return (!product.IsEmpty() && !serial.IsEmpty()); } static JSON* FindTaggedData(JSON* data, const char** tag_names, const char** qtags, int num_qtags) { if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array) return NULL; JSON* tagged_item = data->GetFirstItem(); while (tagged_item) { JSON* tags = tagged_item->GetItemByName("tags"); if (tags->Type == JSON_Array && num_qtags == tags->GetArraySize()) { // Check for a full tag match on each item int num_matches = 0; for (int k=0; kGetFirstItem(); while (tag) { JSON* tagval = tag->GetFirstItem(); if (tagval && tagval->Name == tag_names[k]) { if (tagval->Value == qtags[k]) num_matches++; break; } tag = tags->GetNextItem(tag); } } // if all tags were matched then copy the values into this Profile if (num_matches == num_qtags) { JSON* vals = tagged_item->GetItemByName("vals"); return vals; } } tagged_item = data->GetNextItem(tagged_item); } return NULL; } static void FilterTaggedData(JSON* data, const char* tag_name, const char* qtag, Array& items) { if (data == NULL || !(data->Name == "TaggedData") || data->Type != JSON_Array) return; JSON* tagged_item = data->GetFirstItem(); while (tagged_item) { JSON* tags = tagged_item->GetItemByName("tags"); if (tags->Type == JSON_Array) { // Check for a tag match on the requested tag JSON* tag = tags->GetFirstItem(); while (tag) { JSON* tagval = tag->GetFirstItem(); if (tagval && tagval->Name == tag_name) { if (tagval->Value == qtag) { // Add this item to the output list items.PushBack(tagged_item); } break; } tag = tags->GetNextItem(tag); } } tagged_item = data->GetNextItem(tagged_item); } } //----------------------------------------------------------------------------- // ***** ProfileManager ProfileManager::ProfileManager() { Changed = false; } ProfileManager::~ProfileManager() { ClearCache(); } ProfileManager* ProfileManager::Create() { return new ProfileManager(); } // Clear the local profile cache void ProfileManager::ClearCache() { Lock::Locker lockScope(&ProfileLock); //ProfileCache.Clear(); if (ProfileCache) { //ProfileCache->Release(); ProfileCache = NULL; } Changed = false; } // Returns a profile with all system default values Profile* ProfileManager::GetDefaultProfile(const DeviceBase* device) { // In the absence of any data, set some reasonable profile defaults. // However, this is not future proof and developers should still // provide reasonable default values for queried fields. Profile* profile = CreateProfile(); profile->SetValue(OVR_KEY_USER, "default"); profile->SetValue(OVR_KEY_NAME, "Default"); profile->SetValue(OVR_KEY_GENDER, OVR_DEFAULT_GENDER); profile->SetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT); profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, 1.675f); profile->SetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD); float dist[2] = {OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL}; profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, dist, 2); //profile->SetFloatValue(OVR_KEY_NECK_TO_EYE_VERTICAL, 0.12f); // TODO: Provide device specific defaults OVR_UNUSED(device); // DK1 default //profile->SetValue("EyeCup", "A"); return profile; } // 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(bool create) { Lock::Locker lockScope(&ProfileLock); ClearCache(); String path = GetProfilePath(false); Ptr root = *JSON::Load(path); if (root == NULL) { path = GetBaseOVRPath(false) + "/Profiles.json"; // look for legacy profile root = *JSON::Load(path); if (root == NULL) { if (create) { // Generate a skeleton profile database root = *JSON::CreateObject(); root->AddNumberItem("Oculus Profile Version", 2.0); root->AddItem("Users", JSON::CreateArray()); root->AddItem("TaggedData", JSON::CreateArray()); ProfileCache = root; } return; } // Verify the legacy version JSON* version_item = root->GetFirstItem(); if (version_item->Name == "Oculus Profile Version") { int major = atoi(version_item->Value.ToCStr()); if (major != 1) return; // don't use the file on unsupported major version number } else { return; // invalid file } // Convert the legacy format to the new database format LoadV1Profiles(root); } else { // Verify the file format and version JSON* version_item = root->GetFirstItem(); if (version_item->Name == "Oculus Profile Version") { int major = atoi(version_item->Value.ToCStr()); if (major != 2) return; // don't use the file on unsupported major version number } else { return; // invalid file } ProfileCache = root; // store the database contents for traversal } } void ProfileManager::LoadV1Profiles(JSON* v1) { JSON* item0 = v1->GetFirstItem(); JSON* item1 = v1->GetNextItem(item0); JSON* item2 = v1->GetNextItem(item1); // Create the new profile database Ptr root = *JSON::CreateObject(); root->AddNumberItem("Oculus Profile Version", 2.0); root->AddItem("Users", JSON::CreateArray()); root->AddItem("TaggedData", JSON::CreateArray()); ProfileCache = root; const char* default_dk1_user = item1->Value; // Read the number of profiles int profileCount = (int)item2->dValue; JSON* profileItem = item2; for (int p=0; pGetNextItem(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 } // Read the user profile fields if (CreateUser(profileName, profileName)) { const char* tag_names[2] = {"User", "Product"}; const char* tags[2]; tags[0] = profileName; Ptr user_profile = *CreateProfile(); user_profile->SetValue(OVR_KEY_NAME, profileName); float neckeye[2] = { 0, 0 }; item = profileItem->GetNextItem(item); while (item) { if (item->Type != JSON_Object) { if (item->Name == OVR_KEY_PLAYER_HEIGHT) { // Add an explicit eye height } if (item->Name == "NeckEyeHori") neckeye[0] = (float)item->dValue; else if (item->Name == "NeckEyeVert") neckeye[1] = (float)item->dValue; else user_profile->SetValue(item); } else { // Add the user/device tag values const char* device_name = item->Name.ToCStr(); Ptr device_profile = *CreateProfile(); JSON* device_item = item->GetFirstItem(); while (device_item) { device_profile->SetValue(device_item); device_item = item->GetNextItem(device_item); } tags[1] = device_name; SetTaggedProfile(tag_names, tags, 2, device_profile); } item = profileItem->GetNextItem(item); } // Add an explicit eye-height field float player_height = user_profile->GetFloatValue(OVR_KEY_PLAYER_HEIGHT, OVR_DEFAULT_PLAYER_HEIGHT); if (player_height > 0) { char gender[16]; user_profile->GetValue(OVR_KEY_GENDER, gender, 16); 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 (OVR_strcmp(gender, "Female") == 0) head_height = FEMALE_AVG_HEAD_HEIGHT; else head_height = MALE_AVG_HEAD_HEIGHT; float skull = EYE_TO_HEADTOP_RATIO * head_height; float eye_height = player_height - skull; user_profile->SetFloatValue(OVR_KEY_EYE_HEIGHT, eye_height); } // Convert NeckEye values to an array if (neckeye[0] > 0 && neckeye[1] > 0) user_profile->SetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2); // Add the user tag values SetTaggedProfile(tag_names, tags, 1, user_profile); } } } // since V1 profiles were only for DK1, the assign the user to all DK1's const char* tag_names[1] = { "Product" }; const char* tags[1] = { "RiftDK1" }; Ptr product_profile = *CreateProfile(); product_profile->SetValue("DefaultUser", default_dk1_user); SetTaggedProfile(tag_names, tags, 1, product_profile); } // Returns the number of stored profiles for this device type int ProfileManager::GetUserCount() { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return 0; } JSON* users = ProfileCache->GetItemByName("Users"); if (users == NULL) return 0; return users->GetItemCount(); } bool ProfileManager::CreateUser(const char* user, const char* name) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(true); if (ProfileCache == NULL) return false; } JSON* users = ProfileCache->GetItemByName("Users"); if (users == NULL) { // Generate the User section users = JSON::CreateArray(); ProfileCache->AddItem("Users", users); //TODO: Insert this before the TaggedData } // Search for the pre-existence of this user JSON* user_item = users->GetFirstItem(); int index = 0; while (user_item) { JSON* userid = user_item->GetItemByName("User"); int compare = OVR_strcmp(user, userid->Value); if (compare == 0) { // The user already exists so simply update the fields JSON* name_item = user_item->GetItemByName("Name"); if (name_item && OVR_strcmp(name, name_item->Value) != 0) { name_item->Value = name; Changed = true; } return true; } else if (compare < 0) { // A new user should be placed before this item break; } user_item = users->GetNextItem(user_item); index++; } // Create and fill the user struct JSON* new_user = JSON::CreateObject(); new_user->AddStringItem(OVR_KEY_USER, user); new_user->AddStringItem(OVR_KEY_NAME, name); // user_item->AddStringItem("Password", password); if (user_item == NULL) users->AddArrayElement(new_user); else users->InsertArrayElement(index, new_user); Changed = true; return true; } // Returns the user id of a specific user 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::GetUser(unsigned int index) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return NULL; } JSON* users = ProfileCache->GetItemByName("Users"); if (users && index < users->GetItemCount()) { JSON* user_item = users->GetItemByIndex(index); if (user_item) { JSON* user = user_item->GetFirstItem(); if (user) { JSON* userid = user_item->GetItemByName(OVR_KEY_USER); if (userid) return userid->Value.ToCStr(); } } } return NULL; } bool ProfileManager::RemoveUser(const char* user) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return true; } JSON* users = ProfileCache->GetItemByName("Users"); if (users == NULL) return true; // Remove this user from the User table JSON* user_item = users->GetFirstItem(); while (user_item) { JSON* userid = user_item->GetItemByName("User"); if (OVR_strcmp(user, userid->Value) == 0) { // Delete the user entry user_item->RemoveNode(); user_item->Release(); Changed = true; break; } user_item = users->GetNextItem(user_item); } // Now remove all data entries with this user tag JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); Array user_items; FilterTaggedData(tagged_data, "User", user, user_items); for (unsigned int i=0; iRemoveNode(); user_items[i]->Release(); Changed = true; } return Changed; } Profile* ProfileManager::CreateProfile() { Profile* profile = new Profile(); return profile; } // Returns the name of the profile that is marked as the current default user. const char* ProfileManager::GetDefaultUser(const DeviceBase* device) { const char* tag_names[2] = {"Product", "Serial"}; const char* tags[2]; String product; String serial; if (!GetDeviceTags(device, product, serial)) return NULL; const char* product_str = product.IsEmpty() ? NULL : product.ToCStr(); const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr(); if (product_str && serial_str) { tags[0] = product_str; tags[1] = serial_str; // Look for a default user on this specific device Ptr p = *GetTaggedProfile(tag_names, tags, 2); if (p == NULL) { // Look for a default user on this product p = *GetTaggedProfile(tag_names, tags, 1); } if (p) { const char* user = p->GetValue("DefaultUser"); if (user != NULL && user[0] != 0) { TempBuff = user; return TempBuff.ToCStr(); } } } return NULL; } //----------------------------------------------------------------------------- bool ProfileManager::SetDefaultUser(const DeviceBase* device, const char* user) { const char* tag_names[2] = {"Product", "Serial"}; const char* tags[2]; String product; String serial; if (!GetDeviceTags(device, product, serial)) return NULL; const char* product_str = product.IsEmpty() ? NULL : product.ToCStr(); const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr(); if (product_str && serial_str) { tags[0] = product_str; tags[1] = serial_str; Ptr p = *CreateProfile(); p->SetValue("DefaultUser", user); return SetTaggedProfile(tag_names, tags, 2, p); } return false; } //----------------------------------------------------------------------------- Profile* ProfileManager::GetTaggedProfile(const char** tag_names, const char** tags, int num_tags) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return NULL; } JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); OVR_ASSERT(tagged_data); if (tagged_data == NULL) return NULL; Profile* profile = new Profile(); JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags); if (vals) { JSON* item = vals->GetFirstItem(); while (item) { //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr()); //profile->Settings.Set(item->Name, item->Value); profile->SetValue(item); item = vals->GetNextItem(item); } return profile; } else { profile->Release(); return NULL; } } //----------------------------------------------------------------------------- bool ProfileManager::SetTaggedProfile(const char** tag_names, const char** tags, int num_tags, Profile* profile) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(true); if (ProfileCache == NULL) return false; // TODO: Generate a new profile DB } JSON* tagged_data = ProfileCache->GetItemByName("TaggedData"); OVR_ASSERT(tagged_data); if (tagged_data == NULL) return false; // Get the cached tagged data section JSON* vals = FindTaggedData(tagged_data, tag_names, tags, num_tags); if (vals == NULL) { JSON* tagged_item = JSON::CreateObject(); JSON* taglist = JSON::CreateArray(); for (int i=0; iAddStringItem(tag_names[i], tags[i]); taglist->AddArrayElement(k); } vals = JSON::CreateObject(); tagged_item->AddItem("tags", taglist); tagged_item->AddItem("vals", vals); tagged_data->AddArrayElement(tagged_item); } // Now add or update each profile setting in cache for (unsigned int i=0; iValues.GetSize(); i++) { JSON* value = profile->Values[i]; bool found = false; JSON* item = vals->GetFirstItem(); while (item) { if (value->Name == item->Name) { // Don't allow a pre-existing type to be overridden OVR_ASSERT(value->Type == item->Type); if (value->Type == item->Type) { // Check for the same value if (value->Type == JSON_Array) { // Update each array item if (item->GetArraySize() == value->GetArraySize()) { // Update each value (assumed to be basic types and not array of objects) JSON* value_element = value->GetFirstItem(); JSON* item_element = item->GetFirstItem(); while (item_element && value_element) { if (value_element->Type == JSON_String) { if (item_element->Value != value_element->Value) { // Overwrite the changed value and mark for file update item_element->Value = value_element->Value; Changed = true; } } else { if (item_element->dValue != value_element->dValue) { // Overwrite the changed value and mark for file update item_element->dValue = value_element->dValue; Changed = true; } } value_element = value->GetNextItem(value_element); item_element = item->GetNextItem(item_element); } } else { // if the array size changed, simply create a new one // TODO: Create the new array } } else if (value->Type == JSON_String) { if (item->Value != value->Value) { // Overwrite the changed value and mark for file update item->Value = value->Value; Changed = true; } } else { if (item->dValue != value->dValue) { // Overwrite the changed value and mark for file update item->dValue = value->dValue; Changed = true; } } } else { return false; } found = true; break; } item = vals->GetNextItem(item); } if (!found) { // Add the new value if (value->Type == JSON_String) vals->AddStringItem(value->Name, value->Value); else if (value->Type == JSON_Bool) vals->AddBoolItem(value->Name, (value->dValue != 0)); else if (value->Type == JSON_Array) vals->AddItem(value->Name, value->Copy()); else vals->AddNumberItem(value->Name, value->dValue); Changed = true; } } return true; } //----------------------------------------------------------------------------- Profile* ProfileManager::GetProfile(const DeviceBase* device, const char* user) { Lock::Locker lockScope(&ProfileLock); if (ProfileCache == NULL) { // Load the cache LoadCache(false); if (ProfileCache == NULL) return NULL; } Profile* profile = new Profile(); if (device) { if (!profile->LoadDeviceProfile(device) && (user == NULL)) { profile->Release(); return NULL; } } if (user) { String product; String serial; GetDeviceTags(device, product, serial); const char* product_str = product.IsEmpty() ? NULL : product.ToCStr(); const char* serial_str = serial.IsEmpty() ? NULL : serial.ToCStr(); if (!profile->LoadProfile(ProfileCache.GetPtr(), user, product_str, serial_str)) { profile->Release(); return NULL; } } return profile; } //----------------------------------------------------------------------------- // ***** Profile Profile::~Profile() { ValMap.Clear(); for (unsigned int i=0; iRelease(); Values.Clear(); } bool Profile::Close() { // TODO: return true; } //----------------------------------------------------------------------------- void Profile::CopyItems(JSON* root, String prefix) { JSON* item = root->GetFirstItem(); while (item) { String item_name; if (prefix.IsEmpty()) item_name = item->Name; else item_name = prefix + "." + item->Name; if (item->Type == JSON_Object) { // recursively copy the children CopyItems(item, item_name); } else { //Settings.Set(item_name, item->Value); SetValue(item); } item = root->GetNextItem(item); } } //----------------------------------------------------------------------------- bool Profile::LoadDeviceFile(unsigned int device_id, const char* serial) { if (serial[0] == 0) return false; String path = GetBaseOVRPath(false); path += "/Devices.json"; // Load the device profiles Ptr root = *JSON::Load(path); if (root == NULL) return false; // Quick sanity check of the file type and format before we parse it JSON* version = root->GetFirstItem(); if (version && version->Name == "Oculus Device Profile Version") { int major = atoi(version->Value.ToCStr()); if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION) return false; // don't parse the file on unsupported major version number } else { return false; } JSON* device = root->GetNextItem(version); while (device) { if (device->Name == "Device") { JSON* product_item = device->GetItemByName("ProductID"); JSON* serial_item = device->GetItemByName("Serial"); if (product_item && serial_item && (product_item->dValue == device_id) && (serial_item->Value == serial)) { // found the entry for this device so recursively copy all the settings to the profile CopyItems(device, ""); return true; } } device = root->GetNextItem(device); } return false; } //----------------------------------------------------------------------------- static int BCDByte(unsigned int byte) { int digit1 = (byte >> 4) & 0x000f; int digit2 = byte & 0x000f; int decimal = digit1 * 10 + digit2; return decimal; } //----------------------------------------------------------------------------- bool Profile::LoadDeviceProfile(const DeviceBase* device) { bool success = false; if (device == NULL) return false; SensorDevice* sensor = NULL; if (device->GetType() == Device_HMD) { // Convert the HMD device to Sensor sensor = ((HMDDevice*)device)->GetSensor(); device = sensor; if (device == NULL) return false; } if (device->GetType() == Device_Sensor) { SensorDevice* sensor = (SensorDevice*)device; SensorInfo sinfo; sensor->GetDeviceInfo(&sinfo); int dev_major = BCDByte((sinfo.Version >> 8) & 0x00ff); OVR_UNUSED(dev_major); int dev_minor = BCDByte(sinfo.Version & 0xff); if (dev_minor > 18) { // If the firmware supports hardware stored profiles then grab the device profile // from the sensor // TBD: Implement this } else { // Grab the model and serial number from the device and use it to access the device // profile file stored on the local machine success = LoadDeviceFile(sinfo.ProductId, sinfo.SerialNumber); } } if (sensor) sensor->Release(); // release the sensor handle return success; } //----------------------------------------------------------------------------- bool Profile::LoadUser(JSON* root, const char* user, const char* model_name, const char* device_serial) { if (user == NULL) return false; // For legacy files, convert to old style names //if (model_name && OVR_strcmp(model_name, "Oculus Rift DK1") == 0) // model_name = "RiftDK1"; bool user_found = false; JSON* data = root->GetItemByName("TaggedData"); if (data) { const char* tag_names[3]; const char* tags[3]; tag_names[0] = "User"; tags[0] = user; int num_tags = 1; if (model_name) { tag_names[num_tags] = "Product"; tags[num_tags] = model_name; num_tags++; } if (device_serial) { tag_names[num_tags] = "Serial"; tags[num_tags] = device_serial; num_tags++; } // Retrieve all tag permutations for (int combos=1; combos<=num_tags; combos++) { for (int i=0; i<(num_tags - combos + 1); i++) { JSON* vals = FindTaggedData(data, tag_names+i, tags+i, combos); if (vals) { if (i==0) // This tag-combination contains a user match user_found = true; // Add the values to the Profile. More specialized multi-tag values // will take precedence over and overwrite generalized ones // For example: ("Me","RiftDK1").IPD would overwrite ("Me").IPD JSON* item = vals->GetFirstItem(); while (item) { //printf("Add %s, %s\n", item->Name.ToCStr(), item->Value.ToCStr()); //Settings.Set(item->Name, item->Value); SetValue(item); item = vals->GetNextItem(item); } } } } } if (user_found) SetValue(OVR_KEY_USER, user); return user_found; } //----------------------------------------------------------------------------- bool Profile::LoadProfile(JSON* root, const char* user, const char* device_model, const char* device_serial) { if (!LoadUser(root, user, device_model, device_serial)) return false; return true; } //----------------------------------------------------------------------------- char* Profile::GetValue(const char* key, char* val, int val_length) const { JSON* value = NULL; if (ValMap.Get(key, &value)) { OVR_strcpy(val, val_length, value->Value.ToCStr()); return val; } else { val[0] = 0; return NULL; } } //----------------------------------------------------------------------------- const char* Profile::GetValue(const char* key) { // Non-reentrant query. The returned buffer can only be used until the next call // to GetValue() JSON* value = NULL; if (ValMap.Get(key, &value)) { TempVal = value->Value; return TempVal.ToCStr(); } else { return NULL; } } //----------------------------------------------------------------------------- int Profile::GetNumValues(const char* key) const { JSON* value = NULL; if (ValMap.Get(key, &value)) { if (value->Type == JSON_Array) return value->GetArraySize(); else return 1; } else return 0; } //----------------------------------------------------------------------------- bool Profile::GetBoolValue(const char* key, bool default_val) const { JSON* value = NULL; if (ValMap.Get(key, &value) && value->Type == JSON_Bool) return (value->dValue != 0); else return default_val; } //----------------------------------------------------------------------------- int Profile::GetIntValue(const char* key, int default_val) const { JSON* value = NULL; if (ValMap.Get(key, &value) && value->Type == JSON_Number) return (int)(value->dValue); else return default_val; } //----------------------------------------------------------------------------- float Profile::GetFloatValue(const char* key, float default_val) const { JSON* value = NULL; if (ValMap.Get(key, &value) && value->Type == JSON_Number) return (float)(value->dValue); else return default_val; } //----------------------------------------------------------------------------- int Profile::GetFloatValues(const char* key, float* values, int num_vals) const { JSON* value = NULL; if (ValMap.Get(key, &value) && value->Type == JSON_Array) { int val_count = Alg::Min(value->GetArraySize(), num_vals); JSON* item = value->GetFirstItem(); int count=0; while (item && count < val_count) { if (item->Type == JSON_Number) values[count] = (float)item->dValue; else break; count++; item = value->GetNextItem(item); } return count; } else { return 0; } } //----------------------------------------------------------------------------- double Profile::GetDoubleValue(const char* key, double default_val) const { JSON* value = NULL; if (ValMap.Get(key, &value) && value->Type == JSON_Number) return value->dValue; else return default_val; } //----------------------------------------------------------------------------- int Profile::GetDoubleValues(const char* key, double* values, int num_vals) const { JSON* value = NULL; if (ValMap.Get(key, &value) && value->Type == JSON_Array) { int val_count = Alg::Min(value->GetArraySize(), num_vals); JSON* item = value->GetFirstItem(); int count=0; while (item && count < val_count) { if (item->Type == JSON_Number) values[count] = item->dValue; else break; count++; item = value->GetNextItem(item); } return count; } else { return 0; } } //----------------------------------------------------------------------------- void Profile::SetValue(JSON* val) { if (val->Type == JSON_Number) SetDoubleValue(val->Name, val->dValue); else if (val->Type == JSON_Bool) SetBoolValue(val->Name, (val->dValue != 0)); else if (val->Type == JSON_String) SetValue(val->Name, val->Value); else if (val->Type == JSON_Array) { if (val == NULL) return; // Create a copy of the array JSON* value = val->Copy(); Values.PushBack(value); ValMap.Set(value->Name, value); } } //----------------------------------------------------------------------------- void Profile::SetValue(const char* key, const char* val) { if (key == NULL || val == NULL) return; JSON* value = NULL; if (ValMap.Get(key, &value)) { value->Value = val; } else { value = JSON::CreateString(val); value->Name = key; Values.PushBack(value); ValMap.Set(key, value); } } //----------------------------------------------------------------------------- void Profile::SetBoolValue(const char* key, bool val) { if (key == NULL) return; JSON* value = NULL; if (ValMap.Get(key, &value)) { value->dValue = val; } else { value = JSON::CreateBool(val); value->Name = key; Values.PushBack(value); ValMap.Set(key, value); } } //----------------------------------------------------------------------------- void Profile::SetIntValue(const char* key, int val) { SetDoubleValue(key, val); } //----------------------------------------------------------------------------- void Profile::SetFloatValue(const char* key, float val) { SetDoubleValue(key, val); } //----------------------------------------------------------------------------- void Profile::SetFloatValues(const char* key, const float* vals, int num_vals) { JSON* value = NULL; int val_count = 0; if (ValMap.Get(key, &value)) { if (value->Type == JSON_Array) { // truncate the existing array if fewer entries provided int num_existing_vals = value->GetArraySize(); for (int i=num_vals; iRemoveLast(); JSON* item = value->GetFirstItem(); while (item && val_count < num_vals) { if (item->Type == JSON_Number) item->dValue = vals[val_count]; item = value->GetNextItem(item); val_count++; } } else { return; // Maybe we should change the data type? } } else { value = JSON::CreateArray(); value->Name = key; Values.PushBack(value); ValMap.Set(key, value); } for (; val_count < num_vals; val_count++) value->AddArrayNumber(vals[val_count]); } //----------------------------------------------------------------------------- void Profile::SetDoubleValue(const char* key, double val) { JSON* value = NULL; if (ValMap.Get(key, &value)) { value->dValue = val; } else { value = JSON::CreateNumber(val); value->Name = key; Values.PushBack(value); ValMap.Set(key, value); } } //----------------------------------------------------------------------------- void Profile::SetDoubleValues(const char* key, const double* vals, int num_vals) { JSON* value = NULL; int val_count = 0; if (ValMap.Get(key, &value)) { if (value->Type == JSON_Array) { // truncate the existing array if fewer entries provided int num_existing_vals = value->GetArraySize(); for (int i=num_vals; iRemoveLast(); JSON* item = value->GetFirstItem(); while (item && val_count < num_vals) { if (item->Type == JSON_Number) item->dValue = vals[val_count]; item = value->GetNextItem(item); val_count++; } } else { return; // Maybe we should change the data type? } } else { value = JSON::CreateArray(); value->Name = key; Values.PushBack(value); ValMap.Set(key, value); } for (; val_count < num_vals; val_count++) value->AddArrayNumber(vals[val_count]); } } // OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Profile.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Profile.h new file mode 100644 index 0000000..82ae50d --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Profile.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Profile.h 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 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. Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Profile_h #define OVR_Profile_h #include "OVR_DeviceConstants.h" #include "Kernel/OVR_String.h" #include "Kernel/OVR_RefCount.h" #include "Kernel/OVR_Array.h" #include "Kernel/OVR_StringHash.h" namespace OVR { class Profile; class DeviceBase; class JSON; // ----------------------------------------------------------------------------- // ***** 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; Ptr ProfileCache; bool Changed; String TempBuff; public: static ProfileManager* Create(); int GetUserCount(); const char* GetUser(unsigned int index); bool CreateUser(const char* user, const char* name); bool RemoveUser(const char* user); const char* GetDefaultUser(const DeviceBase* device); bool SetDefaultUser(const DeviceBase* device, const char* user); virtual Profile* CreateProfile(); Profile* GetProfile(const DeviceBase* device, const char* user); Profile* GetDefaultProfile(const DeviceBase* device); Profile* GetTaggedProfile(const char** key_names, const char** keys, int num_keys); bool SetTaggedProfile(const char** key_names, const char** keys, int num_keys, Profile* profile); bool GetDeviceTags(const DeviceBase* device, String& product, String& serial); protected: ProfileManager(); ~ProfileManager(); String GetProfilePath(bool create_dir); void LoadCache(bool create); void ClearCache(); void LoadV1Profiles(JSON* v1); }; //------------------------------------------------------------------- // ***** 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 { protected: OVR::Hash ValMap; OVR::Array Values; OVR::String TempVal; public: ~Profile(); int GetNumValues(const char* key) const; const char* GetValue(const char* key); char* GetValue(const char* key, char* val, int val_length) const; bool GetBoolValue(const char* key, bool default_val) const; int GetIntValue(const char* key, int default_val) const; float GetFloatValue(const char* key, float default_val) const; int GetFloatValues(const char* key, float* values, int num_vals) const; double GetDoubleValue(const char* key, double default_val) const; int GetDoubleValues(const char* key, double* values, int num_vals) const; void SetValue(const char* key, const char* val); void SetBoolValue(const char* key, bool val); void SetIntValue(const char* key, int val); void SetFloatValue(const char* key, float val); void SetFloatValues(const char* key, const float* vals, int num_vals); void SetDoubleValue(const char* key, double val); void SetDoubleValues(const char* key, const double* vals, int num_vals); bool Close(); protected: Profile() {}; void SetValue(JSON* val); static bool LoadProfile(const DeviceBase* device, const char* user, Profile** profile); void CopyItems(JSON* root, String prefix); bool LoadDeviceFile(unsigned int device_id, const char* serial); bool LoadDeviceProfile(const DeviceBase* device); bool LoadProfile(JSON* root, const char* user, const char* device_model, const char* device_serial); bool LoadUser(JSON* root, const char* user, const char* device_name, const char* device_serial); friend class ProfileManager; }; // # defined() check for CAPI compatibility near term that re-defines these // for now. To be unified. #if !defined(OVR_KEY_USER) #define OVR_KEY_USER "User" #define OVR_KEY_NAME "Name" #define OVR_KEY_GENDER "Gender" #define OVR_KEY_PLAYER_HEIGHT "PlayerHeight" #define OVR_KEY_EYE_HEIGHT "EyeHeight" #define OVR_KEY_IPD "IPD" #define OVR_KEY_NECK_TO_EYE_DISTANCE "NeckEyeDistance" #define OVR_KEY_EYE_RELIEF_DIAL "EyeReliefDial" #define OVR_KEY_EYE_TO_NOSE_DISTANCE "EyeToNoseDist" #define OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE "MaxEyeToPlateDist" #define OVR_KEY_EYE_CUP "EyeCup" #define OVR_KEY_CUSTOM_EYE_RENDER "CustomEyeRender" #define OVR_DEFAULT_GENDER "Male" #define OVR_DEFAULT_PLAYER_HEIGHT 1.778f #define OVR_DEFAULT_EYE_HEIGHT 1.675f #define OVR_DEFAULT_IPD 0.064f #define OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL 0.09f #define OVR_DEFAULT_NECK_TO_EYE_VERTICAL 0.15f #define OVR_DEFAULT_EYE_RELIEF_DIAL 3 #endif // OVR_KEY_USER String GetBaseOVRPath(bool create_dir); } #endif // OVR_Profile_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Recording.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Recording.cpp new file mode 100644 index 0000000..9c8772a --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Recording.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Recording.h Content : Support for recording sensor + camera data Created : May 12, 2014 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "Kernel/OVR_Math.h" #include "Kernel/OVR_Array.h" #include "OVR_DeviceMessages.h" #include "OVR_Recording.h" namespace OVR { namespace Recording { // global instance that doesn't do anything Recorder r; }} // OVR::Recording \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Recording.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Recording.h new file mode 100644 index 0000000..a0f720a --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Recording.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : Recording.h Content : Support for recording sensor + camera data Created : March 14, 2014 Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_Recording_h #define OVR_Recording_h namespace OVR { namespace Recording { enum RecordingMode { RecordingOff = 0x0, RecordForPlayback = 0x1, RecordForLogging = 0x2 }; }} // OVR::Recording #ifdef ENABLE_RECORDING #include "Recording/Recording_Recorder.h" #else // If Recording is not enabled, then stub it out namespace OVR { struct PositionCalibrationReport; namespace Vision { class CameraIntrinsics; class DistortionCoefficients; class Blob; }; namespace Recording { class Recorder { public: OVR_FORCE_INLINE void RecordCameraParams(const Vision::CameraIntrinsics&, const Vision::DistortionCoefficients&) { } OVR_FORCE_INLINE void RecordLedPositions(const Array&) { } OVR_FORCE_INLINE void RecordUserParams(const Vector3f&, float) { } OVR_FORCE_INLINE void RecordDeviceIfcVersion(UByte) { } OVR_FORCE_INLINE void RecordMessage(const Message&) { } OVR_FORCE_INLINE void RecordCameraFrameUsed(UInt32) { } OVR_FORCE_INLINE void RecordVisionSuccess(UInt32) { } template OVR_FORCE_INLINE void LogData(const char*, const T&) { } OVR_FORCE_INLINE void SetRecordingMode(RecordingMode) { } OVR_FORCE_INLINE RecordingMode GetRecordingMode() { return RecordingOff; } }; extern Recorder r; OVR_FORCE_INLINE Recorder& GetRecorder() { return r; } }} // namespace OVR::Recording #endif // ENABLE_RECORDING #endif // OVR_Recording_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2Impl.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2Impl.cpp new file mode 100644 index 0000000..47daee3 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2Impl.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Sensor2Impl.cpp Content : DK2 sensor device specific implementation. Created : January 21, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Sensor2Impl.h" #include "OVR_SensorImpl_Common.h" #include "OVR_Sensor2ImplUtil.h" #include "Kernel/OVR_Alg.h" //extern FILE *SF_LOG_fp; namespace OVR { //------------------------------------------------------------------------------------- // ***** Oculus Sensor2-specific packet data structures enum { Sensor2_VendorId = Oculus_VendorId, Sensor2_ProductId = 0x0021, Sensor2_BootLoader = 0x1001, Sensor2_DefaultReportRate = 1000, // Hz }; // Messages we care for enum Tracker2MessageType { Tracker2Message_None = 0, Tracker2Message_Sensors = 11, Tracker2Message_Unknown = 0x100, Tracker2Message_SizeError = 0x101, }; struct Tracker2Sensors { UInt16 LastCommandID; UByte NumSamples; UInt16 RunningSampleCount; // Named 'SampleCount' in the firmware docs. SInt16 Temperature; UInt32 SampleTimestamp; TrackerSample Samples[2]; SInt16 MagX, MagY, MagZ; UInt16 FrameCount; UInt32 FrameTimestamp; UByte FrameID; UByte CameraPattern; UInt16 CameraFrameCount; // Named 'CameraCount' in the firmware docs. UInt32 CameraTimestamp; Tracker2MessageType Decode(const UByte* buffer, int size) { if (size < 64) return Tracker2Message_SizeError; LastCommandID = DecodeUInt16(buffer + 1); NumSamples = buffer[3]; RunningSampleCount = DecodeUInt16(buffer + 4); Temperature = DecodeSInt16(buffer + 6); SampleTimestamp = DecodeUInt32(buffer + 8); // Only unpack as many samples as there actually are. UByte iterationCount = (NumSamples > 1) ? 2 : NumSamples; for (UByte i = 0; i < iterationCount; i++) { UnpackSensor(buffer + 12 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ); UnpackSensor(buffer + 20 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ); } MagX = DecodeSInt16(buffer + 44); MagY = DecodeSInt16(buffer + 46); MagZ = DecodeSInt16(buffer + 48); FrameCount = DecodeUInt16(buffer + 50); FrameTimestamp = DecodeUInt32(buffer + 52); FrameID = buffer[56]; CameraPattern = buffer[57]; CameraFrameCount = DecodeUInt16(buffer + 58); CameraTimestamp = DecodeUInt32(buffer + 60); return Tracker2Message_Sensors; } }; struct Tracker2Message { Tracker2MessageType Type; Tracker2Sensors Sensors; }; // Sensor reports data in the following coordinate system: // Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down. // Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right. // We need to convert it to the following RHS coordinate system: // X right, Y Up, Z Back (out of screen) // Vector3f AccelFromBodyFrameUpdate(const Tracker2Sensors& update, UByte sampleNumber) { const TrackerSample& sample = update.Samples[sampleNumber]; float ax = (float)sample.AccelX; float ay = (float)sample.AccelY; float az = (float)sample.AccelZ; return Vector3f(ax, ay, az) * 0.0001f; } Vector3f MagFromBodyFrameUpdate(const Tracker2Sensors& update) { return Vector3f( (float)update.MagX, (float)update.MagY, (float)update.MagZ) * 0.0001f; } Vector3f EulerFromBodyFrameUpdate(const Tracker2Sensors& update, UByte sampleNumber) { const TrackerSample& sample = update.Samples[sampleNumber]; float gx = (float)sample.GyroX; float gy = (float)sample.GyroY; float gz = (float)sample.GyroZ; return Vector3f(gx, gy, gz) * 0.0001f; } bool Sensor2DeviceImpl::decodeTracker2Message(Tracker2Message* message, UByte* buffer, int size) { memset(message, 0, sizeof(Tracker2Message)); if (size < 4) { message->Type = Tracker2Message_SizeError; return false; } switch (buffer[0]) { case Tracker2Message_Sensors: message->Type = message->Sensors.Decode(buffer, size); break; default: message->Type = Tracker2Message_Unknown; break; } return (message->Type < Tracker2Message_Unknown) && (message->Type != Tracker2Message_None); } //------------------------------------------------------------------------------------- // ***** Sensor2Device Sensor2DeviceImpl::Sensor2DeviceImpl(SensorDeviceCreateDesc* createDesc) : SensorDeviceImpl(createDesc), LastNumSamples(0), LastRunningSampleCount(0), FullCameraFrameCount(0), LastCameraTime("C"), LastFrameTime("F"), LastSensorTime("S"), LastFrameTimestamp(0) { // 15 samples ok in min-window for DK2 since it uses microsecond clock. TimeFilter = SensorTimeFilter(SensorTimeFilter::Settings(15)); pCalibration = new SensorCalibration(this); } Sensor2DeviceImpl::~Sensor2DeviceImpl() { delete pCalibration; } void Sensor2DeviceImpl::openDevice() { // Read the currently configured range from sensor. SensorRangeImpl sr(SensorRange(), 0); if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) { sr.Unpack(); sr.GetSensorRange(&CurrentRange); } // Read the currently configured calibration from sensor. SensorFactoryCalibrationImpl sc; if (GetInternalDevice()->GetFeatureReport(sc.Buffer, SensorFactoryCalibrationImpl::PacketSize)) { sc.Unpack(); AccelCalibrationOffset = sc.AccelOffset; GyroCalibrationOffset = sc.GyroOffset; AccelCalibrationMatrix = sc.AccelMatrix; GyroCalibrationMatrix = sc.GyroMatrix; CalibrationTemperature = sc.Temperature; } // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default. SensorDisplayInfoImpl displayInfo; if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) { displayInfo.Unpack(); Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ? Coord_HMD : Coord_Sensor; } Coordinates = Coord_HMD; // TODO temporary to force it behave // Read/Apply sensor config. setCoordinateFrame(Coordinates); setReportRate(Sensor2_DefaultReportRate); setOnboardCalibrationEnabled(false); // Must send DK2 keep-alive. Set Keep-alive at 10 seconds. KeepAliveMuxReport keepAlive; keepAlive.CommandId = 0; keepAlive.INReport = 11; keepAlive.Interval = 10 * 1000; // Device creation is done from background thread so we don't need to add this to the command queue. KeepAliveMuxImpl keepAliveImpl(keepAlive); GetInternalDevice()->SetFeatureReport(keepAliveImpl.Buffer, KeepAliveMuxImpl::PacketSize); // Read the temperature data from the device pCalibration->Initialize(); } bool Sensor2DeviceImpl::SetTrackingReport(const TrackingReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setTrackingReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setTrackingReport(const TrackingReport& data) { TrackingImpl ci(data); return GetInternalDevice()->SetFeatureReport(ci.Buffer, TrackingImpl::PacketSize); } bool Sensor2DeviceImpl::GetTrackingReport(TrackingReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getTrackingReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getTrackingReport(TrackingReport* data) { TrackingImpl ci; if (GetInternalDevice()->GetFeatureReport(ci.Buffer, TrackingImpl::PacketSize)) { ci.Unpack(); *data = ci.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetDisplayReport(const DisplayReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setDisplayReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setDisplayReport(const DisplayReport& data) { DisplayImpl di(data); return GetInternalDevice()->SetFeatureReport(di.Buffer, DisplayImpl::PacketSize); } bool Sensor2DeviceImpl::GetDisplayReport(DisplayReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getDisplayReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getDisplayReport(DisplayReport* data) { DisplayImpl di; if (GetInternalDevice()->GetFeatureReport(di.Buffer, DisplayImpl::PacketSize)) { di.Unpack(); *data = di.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetMagCalibrationReport(const MagCalibrationReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setMagCalibrationReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setMagCalibrationReport(const MagCalibrationReport& data) { MagCalibrationImpl mci(data); return GetInternalDevice()->SetFeatureReport(mci.Buffer, MagCalibrationImpl::PacketSize); } bool Sensor2DeviceImpl::GetMagCalibrationReport(MagCalibrationReport* data) { // direct call if we are already on the device manager thread if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId()) { return getMagCalibrationReport(data); } bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getMagCalibrationReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getMagCalibrationReport(MagCalibrationReport* data) { MagCalibrationImpl mci; if (GetInternalDevice()->GetFeatureReport(mci.Buffer, MagCalibrationImpl::PacketSize)) { mci.Unpack(); *data = mci.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetPositionCalibrationReport(const PositionCalibrationReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setPositionCalibrationReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setPositionCalibrationReport(const PositionCalibrationReport& data) { UByte version = GetDeviceInterfaceVersion(); if (version < 5) { PositionCalibrationImpl_Pre5 pci(data); return GetInternalDevice()->SetFeatureReport(pci.Buffer, PositionCalibrationImpl_Pre5::PacketSize); } PositionCalibrationImpl pci(data); return GetInternalDevice()->SetFeatureReport(pci.Buffer, PositionCalibrationImpl::PacketSize); } bool Sensor2DeviceImpl::getPositionCalibrationReport(PositionCalibrationReport* data) { UByte version = GetDeviceInterfaceVersion(); if (version < 5) { PositionCalibrationImpl_Pre5 pci; if (GetInternalDevice()->GetFeatureReport(pci.Buffer, PositionCalibrationImpl_Pre5::PacketSize)) { pci.Unpack(); *data = pci.Settings; return true; } return false; } PositionCalibrationImpl pci; if (GetInternalDevice()->GetFeatureReport(pci.Buffer, PositionCalibrationImpl::PacketSize)) { pci.Unpack(); *data = pci.Settings; return true; } return false; } bool Sensor2DeviceImpl::GetAllPositionCalibrationReports(Array* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getAllPositionCalibrationReports, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getAllPositionCalibrationReports(Array* data) { PositionCalibrationReport pc; bool result = getPositionCalibrationReport(&pc); if (!result) return false; int positions = pc.NumPositions; data->Clear(); data->Resize(positions); for (int i = 0; i < positions; i++) { result = getPositionCalibrationReport(&pc); if (!result) return false; OVR_ASSERT(pc.NumPositions == positions); (*data)[pc.PositionIndex] = pc; // IMU should be the last one OVR_ASSERT(pc.PositionType == (pc.PositionIndex == positions - 1) ? PositionCalibrationReport::PositionType_IMU : PositionCalibrationReport::PositionType_LED); } return true; } bool Sensor2DeviceImpl::SetCustomPatternReport(const CustomPatternReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setCustomPatternReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setCustomPatternReport(const CustomPatternReport& data) { CustomPatternImpl cpi(data); return GetInternalDevice()->SetFeatureReport(cpi.Buffer, CustomPatternImpl::PacketSize); } bool Sensor2DeviceImpl::GetCustomPatternReport(CustomPatternReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getCustomPatternReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getCustomPatternReport(CustomPatternReport* data) { CustomPatternImpl cpi; if (GetInternalDevice()->GetFeatureReport(cpi.Buffer, CustomPatternImpl::PacketSize)) { cpi.Unpack(); *data = cpi.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetManufacturingReport(const ManufacturingReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setManufacturingReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setManufacturingReport(const ManufacturingReport& data) { ManufacturingImpl mi(data); return GetInternalDevice()->SetFeatureReport(mi.Buffer, ManufacturingImpl::PacketSize); } bool Sensor2DeviceImpl::GetManufacturingReport(ManufacturingReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getManufacturingReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getManufacturingReport(ManufacturingReport* data) { ManufacturingImpl mi; if (GetInternalDevice()->GetFeatureReport(mi.Buffer, ManufacturingImpl::PacketSize)) { mi.Unpack(); *data = mi.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetLensDistortionReport(const LensDistortionReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setLensDistortionReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setLensDistortionReport(const LensDistortionReport& data) { LensDistortionImpl ui(data); return GetInternalDevice()->SetFeatureReport(ui.Buffer, LensDistortionImpl::PacketSize); } bool Sensor2DeviceImpl::GetLensDistortionReport(LensDistortionReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getLensDistortionReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getLensDistortionReport(LensDistortionReport* data) { LensDistortionImpl ui; if (GetInternalDevice()->GetFeatureReport(ui.Buffer, LensDistortionImpl::PacketSize)) { ui.Unpack(); *data = ui.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetUUIDReport(const UUIDReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setUUIDReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setUUIDReport(const UUIDReport& data) { UUIDImpl ui(data); return GetInternalDevice()->SetFeatureReport(ui.Buffer, UUIDImpl::PacketSize); } bool Sensor2DeviceImpl::GetUUIDReport(UUIDReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getUUIDReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getUUIDReport(UUIDReport* data) { UUIDImpl ui; if (GetInternalDevice()->GetFeatureReport(ui.Buffer, UUIDImpl::PacketSize)) { ui.Unpack(); *data = ui.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetKeepAliveMuxReport(const KeepAliveMuxReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setKeepAliveMuxReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setKeepAliveMuxReport(const KeepAliveMuxReport& data) { KeepAliveMuxImpl kami(data); return GetInternalDevice()->SetFeatureReport(kami.Buffer, KeepAliveMuxImpl::PacketSize); } bool Sensor2DeviceImpl::GetKeepAliveMuxReport(KeepAliveMuxReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getKeepAliveMuxReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getKeepAliveMuxReport(KeepAliveMuxReport* data) { KeepAliveMuxImpl kami; if (GetInternalDevice()->GetFeatureReport(kami.Buffer, KeepAliveMuxImpl::PacketSize)) { kami.Unpack(); *data = kami.Settings; return true; } return false; } bool Sensor2DeviceImpl::SetTemperatureReport(const TemperatureReport& data) { // direct call if we are already on the device manager thread if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId()) { return setTemperatureReport(data); } bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setTemperatureReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::setTemperatureReport(const TemperatureReport& data) { TemperatureImpl ti(data); return GetInternalDevice()->SetFeatureReport(ti.Buffer, TemperatureImpl::PacketSize); } bool Sensor2DeviceImpl::getTemperatureReport(TemperatureReport* data) { TemperatureImpl ti; if (GetInternalDevice()->GetFeatureReport(ti.Buffer, TemperatureImpl::PacketSize)) { ti.Unpack(); *data = ti.Settings; return true; } return false; } bool Sensor2DeviceImpl::GetAllTemperatureReports(Array >* data) { // direct call if we are already on the device manager thread if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId()) { return getAllTemperatureReports(data); } bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getAllTemperatureReports, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getAllTemperatureReports(Array >* data) { TemperatureReport t; bool result = getTemperatureReport(&t); if (!result) return false; int bins = t.NumBins, samples = t.NumSamples; data->Clear(); data->Resize(bins); for (int i = 0; i < bins; i++) (*data)[i].Resize(samples); for (int i = 0; i < bins; i++) for (int j = 0; j < samples; j++) { result = getTemperatureReport(&t); if (!result) return false; OVR_ASSERT(t.NumBins == bins && t.NumSamples == samples); (*data)[t.Bin][t.Sample] = t; } return true; } bool Sensor2DeviceImpl::GetGyroOffsetReport(GyroOffsetReport* data) { // direct call if we are already on the device manager thread if (GetCurrentThreadId() == GetManagerImpl()->GetThreadId()) { return getGyroOffsetReport(data); } bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getGyroOffsetReport, &result, data)) { return false; } return result; } bool Sensor2DeviceImpl::getGyroOffsetReport(GyroOffsetReport* data) { GyroOffsetImpl goi; if (GetInternalDevice()->GetFeatureReport(goi.Buffer, GyroOffsetImpl::PacketSize)) { goi.Unpack(); *data = goi.Settings; return true; } return false; } void Sensor2DeviceImpl::onTrackerMessage(Tracker2Message* message) { if (message->Type != Tracker2Message_Sensors) return; const float sampleIntervalTimeUnit = (1.0f / 1000.f); double scaledSampleIntervalTimeUnit = sampleIntervalTimeUnit; Tracker2Sensors& s = message->Sensors; double absoluteTimeSeconds = 0.0; if (SequenceValid) { UInt32 runningSampleCountDelta; if (s.RunningSampleCount < LastRunningSampleCount) { // The running sample count on the device rolled around the 16 bit counter // (expect to happen about once per minute), so RunningSampleCount // needs a high word increment. runningSampleCountDelta = ((((int)s.RunningSampleCount) + 0x10000) - (int)LastRunningSampleCount); } else { runningSampleCountDelta = (s.RunningSampleCount - LastRunningSampleCount); } absoluteTimeSeconds = LastSensorTime.TimeSeconds; scaledSampleIntervalTimeUnit = TimeFilter.ScaleTimeUnit(sampleIntervalTimeUnit); // If we missed a small number of samples, replicate the last sample. if ((runningSampleCountDelta > LastNumSamples) && (runningSampleCountDelta <= 254)) { if (HandlerRef.HasHandlers()) { MessageBodyFrame sensors(this); sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - s.NumSamples * scaledSampleIntervalTimeUnit; sensors.TimeDelta = (float) ((runningSampleCountDelta - LastNumSamples) * scaledSampleIntervalTimeUnit); sensors.Acceleration = LastAcceleration; sensors.RotationRate = LastRotationRate; sensors.MagneticField = LastMagneticField; sensors.Temperature = LastTemperature; pCalibration->Apply(sensors); HandlerRef.Call(sensors); } } } else { LastAcceleration = Vector3f(0); LastRotationRate = Vector3f(0); LastMagneticField= Vector3f(0); LastTemperature = 0; SequenceValid = true; } LastNumSamples = s.NumSamples; LastRunningSampleCount = s.RunningSampleCount; if (HandlerRef.HasHandlers()) { MessageBodyFrame sensors(this); UByte iterations = s.NumSamples; if (s.NumSamples > 2) { iterations = 2; sensors.TimeDelta = (float) ((s.NumSamples - 1) * scaledSampleIntervalTimeUnit); } else { sensors.TimeDelta = (float) scaledSampleIntervalTimeUnit; } for (UByte i = 0; i < iterations; i++) { sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - ( iterations - 1 - i ) * scaledSampleIntervalTimeUnit; sensors.Acceleration = AccelFromBodyFrameUpdate(s, i); sensors.RotationRate = EulerFromBodyFrameUpdate(s, i); sensors.MagneticField= MagFromBodyFrameUpdate(s); sensors.Temperature = s.Temperature * 0.01f; pCalibration->Apply(sensors); HandlerRef.Call(sensors); // TimeDelta for the last two sample is always fixed. sensors.TimeDelta = (float) scaledSampleIntervalTimeUnit; } // Send pixel read only when frame timestamp changes. if (LastFrameTimestamp != s.FrameTimestamp) { MessagePixelRead pixelRead(this); // Prepare message for pixel read pixelRead.PixelReadValue = s.FrameID; pixelRead.RawFrameTime = s.FrameTimestamp; pixelRead.RawSensorTime = s.SampleTimestamp; pixelRead.SensorTimeSeconds = LastSensorTime.TimeSeconds; pixelRead.FrameTimeSeconds = LastFrameTime.TimeSeconds; HandlerRef.Call(pixelRead); LastFrameTimestamp = s.FrameTimestamp; } UInt16 lowFrameCount = (UInt16) FullCameraFrameCount; // Send message only when frame counter changes if (lowFrameCount != s.CameraFrameCount) { // check for the rollover in the counter if (s.CameraFrameCount < lowFrameCount) FullCameraFrameCount += 0x10000; // update the low bits FullCameraFrameCount = (FullCameraFrameCount & ~0xFFFF) | s.CameraFrameCount; MessageExposureFrame vision(this); vision.CameraPattern = s.CameraPattern; vision.CameraFrameCount = FullCameraFrameCount; vision.CameraTimeSeconds = LastCameraTime.TimeSeconds; HandlerRef.Call(vision); } LastAcceleration = sensors.Acceleration; LastRotationRate = sensors.RotationRate; LastMagneticField= sensors.MagneticField; LastTemperature = sensors.Temperature; //LastPixelRead = pixelRead.PixelReadValue; //LastPixelReadTimeStamp = LastFrameTime; } else { if (s.NumSamples != 0) { UByte i = (s.NumSamples > 1) ? 1 : 0; LastAcceleration = AccelFromBodyFrameUpdate(s, i); LastRotationRate = EulerFromBodyFrameUpdate(s, i); LastMagneticField = MagFromBodyFrameUpdate(s); LastTemperature = s.Temperature * 0.01f; } } } // Helper function to handle wrap-around of timestamps from Tracker2Message and convert them // to system time. // - Any timestamps that didn't increment keep their old system time. // - This is a bit tricky since we don't know which one of timestamps has most recent time. // - The first timestamp must be the IMU one; we assume that others can't be too much ahead of it void UpdateDK2Timestamps(SensorTimeFilter& tf, SensorTimestampMapping** timestamps, UInt32 *rawValues, int count) { int updateIndices[4]; int updateCount = 0; int i; double now = Timer::GetSeconds(); OVR_ASSERT(count <= sizeof(updateIndices)/sizeof(int)); // Update timestamp wrapping for any values that changed. for (i = 0; i < count; i++) { UInt32 lowMks = (UInt32)timestamps[i]->TimestampMks; // Low 32-bits are raw old timestamp. if (rawValues[i] != lowMks) { if (i == 0) { // Only check for rollover in the IMU timestamp if (rawValues[i] < lowMks) { LogText("Timestamp %d rollover, was: %u, now: %u\n", i, lowMks, rawValues[i]); timestamps[i]->TimestampMks += 0x100000000; } // Update the low bits timestamps[i]->TimestampMks = (timestamps[i]->TimestampMks & 0xFFFFFFFF00000000) | rawValues[i]; } else { // Take the high bits from the main timestamp first (not a typo in the first argument!) timestamps[i]->TimestampMks = (timestamps[0]->TimestampMks & 0xFFFFFFFF00000000) | rawValues[i]; // Now force it into the reasonable range around the expanded main timestamp if (timestamps[i]->TimestampMks > timestamps[0]->TimestampMks + 0x1000000) timestamps[i]->TimestampMks -= 0x100000000; else if (timestamps[i]->TimestampMks + 0x100000000 < timestamps[0]->TimestampMks + 0x1000000) timestamps[i]->TimestampMks += 0x100000000; } updateIndices[updateCount] = i; updateCount++; } } // TBD: Simplify. Update indices should no longer be needed with new TimeFilter accepting // previous values. // We might want to have multi-element checking time roll-over. static const double mksToSec = 1.0 / 1000000.0; for (int i = 0; i < updateCount; i++) { SensorTimestampMapping& ts = *timestamps[updateIndices[i]]; ts.TimeSeconds = tf.SampleToSystemTime(((double)ts.TimestampMks) * mksToSec, now, ts.TimeSeconds, ts.DebugTag); } } void Sensor2DeviceImpl::OnInputReport(UByte* pData, UInt32 length) { bool processed = false; if (!processed) { Tracker2Message message; if (decodeTracker2Message(&message, pData, length)) { processed = true; // Process microsecond timestamps from DK2 tracker. // Mapped and raw values must correspond to one another in each array. // IMU timestamp must be the first one! SensorTimestampMapping* tsMaps[3] = { &LastSensorTime, &LastCameraTime, &LastFrameTime }; UInt32 tsRawMks[3] = { message.Sensors.SampleTimestamp, message.Sensors.CameraTimestamp, message.Sensors.FrameTimestamp }; // Handle wrap-around and convert samples to system time for any samples that changed. UpdateDK2Timestamps(TimeFilter, tsMaps, tsRawMks, sizeof(tsRawMks)/sizeof(tsRawMks[0])); onTrackerMessage(&message); /* if (SF_LOG_fp) { static UInt32 lastFrameTs = 0; static UInt32 lastCameraTs = 0; if ((lastFrameTs != message.Sensors.FrameTimestamp) || (lastCameraTs = message.Sensors.CameraTimestamp)) fprintf(SF_LOG_fp, "msg cameraTs: 0x%X frameTs: 0x%X sensorTs: 0x%X\n", message.Sensors.CameraTimestamp, message.Sensors.FrameTimestamp, message.Sensors.SampleTimestamp); lastFrameTs = message.Sensors.FrameTimestamp; lastCameraTs = message.Sensors.CameraTimestamp; } */ #if 0 // Checks for DK2 firmware bug. static unsigned SLastSampleTime = 0; if ((SLastSampleTime > message.Sensors.SampleTimestamp) && message.Sensors.SampleTimestamp > 1000000 ) { fprintf(SF_LOG_fp, "*** Sample Timestamp Wrap! ***\n"); OVR_ASSERT (SLastSampleTime <= message.Sensors.SampleTimestamp); } SLastSampleTime = message.Sensors.SampleTimestamp; static unsigned SLastCameraTime = 0; if ((SLastCameraTime > message.Sensors.CameraTimestamp) && message.Sensors.CameraTimestamp > 1000000 ) { fprintf(SF_LOG_fp, "*** Camera Timestamp Wrap! ***\n"); OVR_ASSERT (SLastCameraTime <= message.Sensors.CameraTimestamp); } SLastCameraTime = message.Sensors.CameraTimestamp; static unsigned SLastFrameTime = 0; if ((SLastFrameTime > message.Sensors.FrameTimestamp) && message.Sensors.FrameTimestamp > 1000000 ) { fprintf(SF_LOG_fp, "*** Frame Timestamp Wrap! ***\n"); OVR_ASSERT (SLastFrameTime <= message.Sensors.FrameTimestamp); } SLastFrameTime = message.Sensors.FrameTimestamp; #endif } } } double Sensor2DeviceImpl::OnTicks(double tickSeconds) { if (tickSeconds >= NextKeepAliveTickSeconds) { // Must send DK2 keep-alive. Set Keep-alive at 10 seconds. KeepAliveMuxReport keepAlive; keepAlive.CommandId = 0; keepAlive.INReport = 11; keepAlive.Interval = 10 * 1000; // Device creation is done from background thread so we don't need to add this to the command queue. KeepAliveMuxImpl keepAliveImpl(keepAlive); GetInternalDevice()->SetFeatureReport(keepAliveImpl.Buffer, KeepAliveMuxImpl::PacketSize); // Emit keep-alive every few seconds. double keepAliveDelta = 3.0; // Use 3-second interval. NextKeepAliveTickSeconds = tickSeconds + keepAliveDelta; } return NextKeepAliveTickSeconds - tickSeconds; } /* // TBD: don't report calibration for now, until we figure out the logic between camera and mag yaw correction bool Sensor2DeviceImpl::IsMagCalibrated() { return pCalibration->IsMagCalibrated(); } */ } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2Impl.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2Impl.h new file mode 100644 index 0000000..eb497cd --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2Impl.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Sensor2Impl.h Content : DK2 sensor device specific implementation. Created : January 21, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Sensor2Impl_h #define OVR_Sensor2Impl_h #include "OVR_SensorImpl.h" #include "OVR_SensorCalibration.h" namespace OVR { struct Tracker2Message; //------------------------------------------------------------------------------------- // Used to convert DK2 Mks timestamps to system TimeSeconds struct SensorTimestampMapping { UInt64 TimestampMks; double TimeSeconds; const char* DebugTag; SensorTimestampMapping(const char* debugTag) : TimestampMks(0), TimeSeconds(0.0), DebugTag(debugTag) { } }; //------------------------------------------------------------------------------------- // ***** OVR::Sensor2DeviceImpl // Oculus Sensor2 interface. class Sensor2DeviceImpl : public SensorDeviceImpl { public: Sensor2DeviceImpl(SensorDeviceCreateDesc* createDesc); ~Sensor2DeviceImpl(); // HIDDevice::Notifier interface. virtual void OnInputReport(UByte* pData, UInt32 length); virtual double OnTicks(double tickSeconds); // Get/set feature reports added for DK2. See 'DK2 Firmware Specification' document details. virtual bool SetTrackingReport(const TrackingReport& data); virtual bool GetTrackingReport(TrackingReport* data); virtual bool SetDisplayReport(const DisplayReport& data); virtual bool GetDisplayReport(DisplayReport* data); virtual bool SetMagCalibrationReport(const MagCalibrationReport& data); virtual bool GetMagCalibrationReport(MagCalibrationReport* data); virtual bool SetPositionCalibrationReport(const PositionCalibrationReport& data); virtual bool GetAllPositionCalibrationReports(Array* data); virtual bool SetCustomPatternReport(const CustomPatternReport& data); virtual bool GetCustomPatternReport(CustomPatternReport* data); virtual bool SetKeepAliveMuxReport(const KeepAliveMuxReport& data); virtual bool GetKeepAliveMuxReport(KeepAliveMuxReport* data); virtual bool SetManufacturingReport(const ManufacturingReport& data); virtual bool GetManufacturingReport(ManufacturingReport* data); virtual bool SetUUIDReport(const UUIDReport& data); virtual bool GetUUIDReport(UUIDReport* data); virtual bool SetTemperatureReport(const TemperatureReport& data); virtual bool GetAllTemperatureReports(Array >*); virtual bool GetGyroOffsetReport(GyroOffsetReport* data); virtual bool SetLensDistortionReport(const LensDistortionReport& data); virtual bool GetLensDistortionReport(LensDistortionReport* data); protected: virtual void openDevice(); bool decodeTracker2Message(Tracker2Message* message, UByte* buffer, int size); bool setTrackingReport(const TrackingReport& data); bool getTrackingReport(TrackingReport* data); bool setDisplayReport(const DisplayReport& data); bool getDisplayReport(DisplayReport* data); bool setMagCalibrationReport(const MagCalibrationReport& data); bool getMagCalibrationReport(MagCalibrationReport* data); bool setPositionCalibrationReport(const PositionCalibrationReport& data); bool getPositionCalibrationReport(PositionCalibrationReport* data); bool getAllPositionCalibrationReports(Array* data); bool setCustomPatternReport(const CustomPatternReport& data); bool getCustomPatternReport(CustomPatternReport* data); bool setKeepAliveMuxReport(const KeepAliveMuxReport& data); bool getKeepAliveMuxReport(KeepAliveMuxReport* data); bool setManufacturingReport(const ManufacturingReport& data); bool getManufacturingReport(ManufacturingReport* data); bool setUUIDReport(const UUIDReport& data); bool getUUIDReport(UUIDReport* data); bool setTemperatureReport(const TemperatureReport& data); bool getTemperatureReport(TemperatureReport* data); bool getAllTemperatureReports(Array >*); bool getGyroOffsetReport(GyroOffsetReport* data); bool setLensDistortionReport(const LensDistortionReport& data); bool getLensDistortionReport(LensDistortionReport* data); // Called for decoded messages void onTrackerMessage(Tracker2Message* message); UByte LastNumSamples; UInt16 LastRunningSampleCount; UInt32 FullCameraFrameCount; SensorTimestampMapping LastCameraTime; SensorTimestampMapping LastFrameTime; SensorTimestampMapping LastSensorTime; // Record last frame timestamp to know when to send pixelRead messages. UInt32 LastFrameTimestamp; SensorCalibration *pCalibration; }; } // namespace OVR #endif // OVR_Sensor2Impl_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2ImplUtil.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2ImplUtil.h new file mode 100644 index 0000000..53e9f76 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Sensor2ImplUtil.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Sensor2ImplUtil.h Content : DK2 sensor device feature report utils. Created : January 27, 2014 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Sensor2ImplUtil_h #define OVR_Sensor2ImplUtil_h #include "OVR_Device.h" #include "OVR_SensorImpl_Common.h" #include "Kernel/OVR_Alg.h" namespace OVR { using namespace Alg; // Tracking feature report. struct TrackingImpl { enum { PacketSize = 13 }; UByte Buffer[PacketSize]; TrackingReport Settings; TrackingImpl() { for (int i=0; i> 4) & 0x02); Settings.UseRolling = (Buffer[4] & 0x40) != 0; Settings.ReverseRolling = (Buffer[4] & 0x80) != 0; Settings.HighBrightness = (Buffer[5] & 0x01) != 0; Settings.SelfRefresh = (Buffer[5] & 0x02) != 0; Settings.ReadPixel = (Buffer[5] & 0x04) != 0; Settings.DirectPentile = (Buffer[5] & 0x08) != 0; Settings.Persistence = DecodeUInt16(Buffer+8); Settings.LightingOffset = DecodeUInt16(Buffer+10); Settings.PixelSettle = DecodeUInt16(Buffer+12); Settings.TotalRows = DecodeUInt16(Buffer+14); } }; // MagCalibration feature report. struct MagCalibrationImpl { enum { PacketSize = 52 }; UByte Buffer[PacketSize]; MagCalibrationReport Settings; MagCalibrationImpl() { memset(Buffer, 0, sizeof(Buffer)); Buffer[0] = 14; } MagCalibrationImpl(const MagCalibrationReport& settings) : Settings(settings) { Pack(); } void Pack() { Buffer[0] = 14; EncodeUInt16(Buffer+1, Settings.CommandId); Buffer[3] = Settings.Version; for (int i = 0; i < 3; i++) for (int j = 0; j < 4; j++) { SInt32 value = SInt32(Settings.Calibration.M[i][j] * 1e4f); EncodeSInt32(Buffer + 4 + 4 * (4 * i + j), value); } } void Unpack() { Settings.CommandId = DecodeUInt16(Buffer+1); Settings.Version = Buffer[3]; for (int i = 0; i < 3; i++) for (int j = 0; j < 4; j++) { SInt32 value = DecodeSInt32(Buffer + 4 + 4 * (4 * i + j)); Settings.Calibration.M[i][j] = (float)value * 1e-4f; } } }; //------------------------------------------------------------------------------------- // PositionCalibration feature report. // - Sensor interface versions before 5 do not support Normal and Rotation. struct PositionCalibrationImpl { enum { PacketSize = 30 }; UByte Buffer[PacketSize]; PositionCalibrationReport Settings; PositionCalibrationImpl() { for (int i=0; i> 8); Buffer[3] = UByte(Settings.Version); Vector3d offset = Settings.Offset * 1e4; PackSensor(Buffer + 4, (SInt32) offset.x, (SInt32) offset.y, (SInt32) offset.z); EncodeSInt16(Buffer + 16, SInt16(Settings.Temperature * 1e2)); } void Unpack() { Settings.CommandId = DecodeUInt16(Buffer + 1); Settings.Version = GyroOffsetReport::VersionEnum(Buffer[3]); SInt32 x, y, z; UnpackSensor(Buffer + 4, &x, &y, &z); Settings.Offset = Vector3d(x, y, z) * 1e-4f; Settings.Temperature = DecodeSInt16(Buffer + 16) * 1e-2; } }; } // namespace OVR #endif // OVR_Sensor2ImplUtil_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorCalibration.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorCalibration.cpp new file mode 100644 index 0000000..8c2c99b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorCalibration.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorCalibration.cpp Content : Calibration data implementation for the IMU messages Created : January 28, 2014 Authors : Max Katsev Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_SensorCalibration.h" #include "Kernel/OVR_Log.h" #include "Kernel/OVR_Threads.h" #include namespace OVR { using namespace Alg; const UByte VERSION = 2; const UByte MAX_COMPAT_VERSION = 15; SensorCalibration::SensorCalibration(SensorDevice* pSensor) : MagCalibrated(false), GyroFilter(6000), GyroAutoTemperature(0) { this->pSensor = pSensor; }; void SensorCalibration::Initialize() { // read factory calibration pSensor->GetFactoryCalibration(&AccelOffset, &GyroAutoOffset, &AccelMatrix, &GyroMatrix, &GyroAutoTemperature); // if the headset has an autocalibrated offset, prefer it over the factory defaults GyroOffsetReport gyroReport; bool result = pSensor->GetGyroOffsetReport(&gyroReport); if (result && gyroReport.Version != GyroOffsetReport::Version_NoOffset) { GyroAutoOffset = (Vector3f) gyroReport.Offset; GyroAutoTemperature = (float) gyroReport.Temperature; } // read the temperature tables and prepare the interpolation structures result = pSensor->GetAllTemperatureReports(&TemperatureReports); OVR_ASSERT(result); for (int i = 0; i < 3; i++) Interpolators[i].Initialize(TemperatureReports, i); // read the mag calibration MagCalibrationReport report; result = pSensor->GetMagCalibrationReport(&report); MagCalibrated = result && report.Version > 0; MagMatrix = report.Calibration; if (!MagCalibrated) { // OVR_ASSERT(false); LogError("Magnetometer calibration not found!\n"); } } void SensorCalibration::DebugPrintLocalTemperatureTable() { LogText("TemperatureReports:\n"); for (int i = 0; i < (int)TemperatureReports.GetSize(); i++) { for (int j = 0; j < (int)TemperatureReports[i].GetSize(); j++) { TemperatureReport& tr = TemperatureReports[i][j]; LogText("[%d][%d]: Version=%3d, Bin=%d/%d, " "Sample=%d/%d, TargetTemp=%3.1lf, " "ActualTemp=%4.1lf, " "Offset=(%7.2lf, %7.2lf, %7.2lf), " "Time=%d\n", i, j, tr.Version, tr.Bin, tr.NumBins, tr.Sample, tr.NumSamples, tr.TargetTemperature, tr.ActualTemperature, tr.Offset.x, tr.Offset.y, tr.Offset.z, tr.Time); } } } void SensorCalibration::DebugClearHeadsetTemperatureReports() { OVR_ASSERT(pSensor != NULL); bool result; Array > temperatureReports; pSensor->GetAllTemperatureReports(&temperatureReports); OVR_ASSERT(temperatureReports.GetSize() > 0); OVR_ASSERT(temperatureReports[0].GetSize() > 0); TemperatureReport& tr = TemperatureReports[0][0]; tr.ActualTemperature = 0.0; tr.Time = 0; tr.Version = 0; tr.Offset.x = tr.Offset.y = tr.Offset.z = 0.0; for (UByte i = 0; i < tr.NumBins; i++) { tr.Bin = i; for (UByte j = 0; j < tr.NumSamples; j++) { tr.Sample = j; result = pSensor->SetTemperatureReport(tr); OVR_ASSERT(result); // Need to wait for the tracker board to finish writing to eeprom. Thread::MSleep(50); } } } void SensorCalibration::Apply(MessageBodyFrame& msg) { AutocalibrateGyro(msg); // compute the interpolated offset Vector3f gyroOffset; for (int i = 0; i < 3; i++) gyroOffset[i] = (float) Interpolators[i].GetOffset(msg.Temperature, GyroAutoTemperature, GyroAutoOffset[i]); // apply calibration msg.RotationRate = GyroMatrix.Transform(msg.RotationRate - gyroOffset); msg.Acceleration = AccelMatrix.Transform(msg.Acceleration - AccelOffset); if (MagCalibrated) msg.MagneticField = MagMatrix.Transform(msg.MagneticField); } void SensorCalibration::AutocalibrateGyro(MessageBodyFrame const& msg) { const float alpha = 0.4f; // 1.25f is a scaling factor related to conversion from per-axis comparison to length comparison const float absLimit = 1.25f * 0.349066f; const float noiseLimit = 1.25f * 0.03f; Vector3f gyro = msg.RotationRate; // do a moving average to reject short term noise Vector3f avg = (GyroFilter.IsEmpty()) ? gyro : gyro * alpha + GyroFilter.PeekBack() * (1 - alpha); // Make sure the absolute value is below what is likely motion // Make sure it is close enough to the current average that it is probably noise and not motion if (avg.Length() >= absLimit || (avg - GyroFilter.Mean()).Length() >= noiseLimit) GyroFilter.Clear(); GyroFilter.PushBack(avg); // if had a reasonable number of samples already use it for the current offset if (GyroFilter.GetSize() > GyroFilter.GetCapacity() / 2) { GyroAutoOffset = GyroFilter.Mean(); GyroAutoTemperature = msg.Temperature; // After ~6 seconds of no motion, use the average as the new zero rate offset if (GyroFilter.IsFull()) StoreAutoOffset(); } } void SensorCalibration::StoreAutoOffset() { const double maxDeltaT = 2.5; const double minExtraDeltaT = 0.5; const UInt32 minDelay = 24 * 3600; // 1 day in seconds // find the best bin UPInt binIdx = 0; for (UPInt i = 1; i < TemperatureReports.GetSize(); i++) if (Abs(GyroAutoTemperature - TemperatureReports[i][0].TargetTemperature) < Abs(GyroAutoTemperature - TemperatureReports[binIdx][0].TargetTemperature)) binIdx = i; // find the oldest and newest samples // NB: uninitialized samples have Time == 0, so they will get picked as the oldest UPInt newestIdx = 0, oldestIdx = 0; for (UPInt i = 1; i < TemperatureReports[binIdx].GetSize(); i++) { // if the version is newer - do nothing if (TemperatureReports[binIdx][i].Version > VERSION) return; if (TemperatureReports[binIdx][i].Time > TemperatureReports[binIdx][newestIdx].Time) newestIdx = i; if (TemperatureReports[binIdx][i].Time < TemperatureReports[binIdx][oldestIdx].Time) oldestIdx = i; } TemperatureReport& oldestReport = TemperatureReports[binIdx][oldestIdx]; TemperatureReport& newestReport = TemperatureReports[binIdx][newestIdx]; OVR_ASSERT((oldestReport.Sample == 0 && newestReport.Sample == 0 && newestReport.Version == 0) || oldestReport.Sample == (newestReport.Sample + 1) % newestReport.NumSamples); bool writeSuccess = false; UInt32 now = (UInt32) time(0); if (now - newestReport.Time > minDelay) { // only write a new sample if the temperature is close enough if (Abs(GyroAutoTemperature - oldestReport.TargetTemperature) < maxDeltaT) { oldestReport.Time = now; oldestReport.ActualTemperature = GyroAutoTemperature; oldestReport.Offset = (Vector3d) GyroAutoOffset; oldestReport.Version = VERSION; writeSuccess = pSensor->SetTemperatureReport(oldestReport); OVR_ASSERT(writeSuccess); } } else { // if the newest sample is too recent - _update_ it if significantly closer to the target temp if (Abs(GyroAutoTemperature - newestReport.TargetTemperature) + minExtraDeltaT < Abs(newestReport.ActualTemperature - newestReport.TargetTemperature)) { // (do not update the time!) newestReport.ActualTemperature = GyroAutoTemperature; newestReport.Offset = (Vector3d) GyroAutoOffset; newestReport.Version = VERSION; writeSuccess = pSensor->SetTemperatureReport(newestReport); OVR_ASSERT(writeSuccess); } } // update the interpolators with the new data // this is not particularly expensive call and would only happen rarely // but if performance is a problem, it's possible to only recompute the data that has changed if (writeSuccess) for (int i = 0; i < 3; i++) Interpolators[i].Initialize(TemperatureReports, i); } const TemperatureReport& median(const Array& temperatureReportsBin, int coord) { Array values; values.Reserve(temperatureReportsBin.GetSize()); for (unsigned i = 0; i < temperatureReportsBin.GetSize(); i++) if (temperatureReportsBin[i].ActualTemperature != 0) values.PushBack(temperatureReportsBin[i].Offset[coord]); if (values.GetSize() > 0) { double med = Median(values); // this is kind of a hack for (unsigned i = 0; i < temperatureReportsBin.GetSize(); i++) if (temperatureReportsBin[i].Offset[coord] == med) return temperatureReportsBin[i]; // if we haven't found the median in the original array, something is wrong OVR_DEBUG_BREAK; } return temperatureReportsBin[0]; } void OffsetInterpolator::Initialize(Array > const& temperatureReports, int coord) { int bins = (int) temperatureReports.GetSize(); Temperatures.Clear(); Temperatures.Reserve(bins); Values.Clear(); Values.Reserve(bins); for (int bin = 0; bin < bins; bin++) { OVR_ASSERT(temperatureReports[bin].GetSize() == temperatureReports[0].GetSize()); const TemperatureReport& report = median(temperatureReports[bin], coord); if (report.Version > 0 && report.Version <= MAX_COMPAT_VERSION) { Temperatures.PushBack(report.ActualTemperature); Values.PushBack(report.Offset[coord]); } } } double OffsetInterpolator::GetOffset(double targetTemperature, double autoTemperature, double autoValue) { const double autoRangeExtra = 1.0; const double minInterpolationDist = 0.5; // difference between current and autocalibrated temperature adjusted for preference over historical data const double adjustedDeltaT = Abs(autoTemperature - targetTemperature) - autoRangeExtra; int count = (int) Temperatures.GetSize(); // handle special cases when we don't have enough data for proper interpolation if (count == 0) return autoValue; if (count == 1) { if (adjustedDeltaT < Abs(Temperatures[0] - targetTemperature)) return autoValue; else return Values[0]; } // first, find the interval that contains targetTemperature // if all points are on the same side of targetTemperature, find the adjacent interval int l; if (targetTemperature < Temperatures[1]) l = 0; else if (targetTemperature >= Temperatures[count - 2]) l = count - 2; else for (l = 1; l < count - 2; l++) if (Temperatures[l] <= targetTemperature && targetTemperature < Temperatures[l+1]) break; int u = l + 1; // extend the interval if it's too small and the interpolation is unreliable if (Temperatures[u] - Temperatures[l] < minInterpolationDist) { if (l > 0 && (u == count - 1 || Temperatures[u] - Temperatures[l - 1] < Temperatures[u + 1] - Temperatures[l])) l--; else if (u < count - 1) u++; } // verify correctness OVR_ASSERT(l >= 0 && u < count); OVR_ASSERT(l == 0 || Temperatures[l] <= targetTemperature); OVR_ASSERT(u == count - 1 || targetTemperature < Temperatures[u]); OVR_ASSERT((l == 0 && u == count - 1) || Temperatures[u] - Temperatures[l] > minInterpolationDist); OVR_ASSERT(Temperatures[l] <= Temperatures[u]); // perform the interpolation double slope; if (Temperatures[u] - Temperatures[l] >= minInterpolationDist) slope = (Values[u] - Values[l]) / (Temperatures[u] - Temperatures[l]); else // avoid a badly conditioned problem slope = 0; if (adjustedDeltaT < Abs(Temperatures[u] - targetTemperature)) // use the autocalibrated value, if it's close return autoValue + slope * (targetTemperature - autoTemperature); else return Values[u] + slope * (targetTemperature - Temperatures[u]); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorCalibration.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorCalibration.h new file mode 100644 index 0000000..31fafed --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorCalibration.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorCalibration.h Content : Calibration data implementation for the IMU messages Created : January 28, 2014 Authors : Max Katsev Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_SensorCalibration_h #define OVR_SensorCalibration_h #include "OVR_Device.h" #include "OVR_SensorFilter.h" namespace OVR { class OffsetInterpolator { public: void Initialize(Array > const& temperatureReports, int coord); double GetOffset(double targetTemperature, double autoTemperature, double autoValue); Array Temperatures; Array Values; }; class SensorCalibration : public NewOverrideBase { public: SensorCalibration(SensorDevice* pSensor); // Load data from the HW and perform the necessary preprocessing void Initialize(); // Apply the calibration void Apply(MessageBodyFrame& msg); // Is mag calibration available? bool IsMagCalibrated() { return MagCalibrated; } protected: void StoreAutoOffset(); void AutocalibrateGyro(MessageBodyFrame const& msg); void DebugPrintLocalTemperatureTable(); void DebugClearHeadsetTemperatureReports(); SensorDevice* pSensor; // Factory calibration data bool MagCalibrated; Matrix4f AccelMatrix, GyroMatrix, MagMatrix; Vector3f AccelOffset; // Temperature based data Array > TemperatureReports; OffsetInterpolator Interpolators[3]; // Autocalibration data SensorFilterf GyroFilter; Vector3f GyroAutoOffset; float GyroAutoTemperature; }; } // namespace OVR #endif //OVR_SensorCalibration_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFilter.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFilter.cpp new file mode 100644 index 0000000..4816dbc --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFilter.cpp @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_SensorFilter.cpp Content : Basic filtering of sensor this->Data Created : March 7, 2013 Authors : Steve LaValle, Anna Yershova, Max Katsev Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_SensorFilter.h" namespace OVR { template Vector3 SensorFilter::Median() const { Vector3 result; T* slice = (T*) OVR_ALLOC(this->ElemCount * sizeof(T)); for (int coord = 0; coord < 3; coord++) { for (int i = 0; i < this->ElemCount; i++) slice[i] = this->Data[i][coord]; result[coord] = Alg::Median(ArrayAdaptor(slice, this->ElemCount)); } OVR_FREE(slice); return result; } // Only the diagonal of the covariance matrix. template Vector3 SensorFilter::Variance() const { Vector3 mean = this->Mean(); Vector3 total; for (int i = 0; i < this->ElemCount; i++) { total.x += (this->Data[i].x - mean.x) * (this->Data[i].x - mean.x); total.y += (this->Data[i].y - mean.y) * (this->Data[i].y - mean.y); total.z += (this->Data[i].z - mean.z) * (this->Data[i].z - mean.z); } return total / (float) this->ElemCount; } template Matrix3 SensorFilter::Covariance() const { Vector3 mean = this->Mean(); Matrix3 total; for (int i = 0; i < this->ElemCount; i++) { total.M[0][0] += (this->Data[i].x - mean.x) * (this->Data[i].x - mean.x); total.M[1][0] += (this->Data[i].y - mean.y) * (this->Data[i].x - mean.x); total.M[2][0] += (this->Data[i].z - mean.z) * (this->Data[i].x - mean.x); total.M[1][1] += (this->Data[i].y - mean.y) * (this->Data[i].y - mean.y); total.M[2][1] += (this->Data[i].z - mean.z) * (this->Data[i].y - mean.y); total.M[2][2] += (this->Data[i].z - mean.z) * (this->Data[i].z - mean.z); } total.M[0][1] = total.M[1][0]; total.M[0][2] = total.M[2][0]; total.M[1][2] = total.M[2][1]; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) total.M[i][j] /= (float) this->ElemCount; return total; } template Vector3 SensorFilter::PearsonCoefficient() const { Matrix3 cov = this->Covariance(); Vector3 pearson; pearson.x = cov.M[0][1]/(sqrt(cov.M[0][0])*sqrt(cov.M[1][1])); pearson.y = cov.M[1][2]/(sqrt(cov.M[1][1])*sqrt(cov.M[2][2])); pearson.z = cov.M[2][0]/(sqrt(cov.M[2][2])*sqrt(cov.M[0][0])); return pearson; } } //namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFilter.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFilter.h new file mode 100644 index 0000000..2ee5c84 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFilter.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_SensorFilter.h Content : Basic filtering of sensor data Created : March 7, 2013 Authors : Steve LaValle, Anna Yershova, Max Katsev Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_SensorFilter_h #define OVR_SensorFilter_h #include "Kernel/OVR_Math.h" #include "Kernel/OVR_Deque.h" #include "Kernel/OVR_Alg.h" namespace OVR { // A base class for filters that maintains a buffer of sensor data taken over time and implements // various simple filters, most of which are linear functions of the data history. // Maintains the running sum of its elements for better performance on large capacity values template class SensorFilterBase : public CircularBuffer { protected: T RunningTotal; // Cached sum of the elements public: SensorFilterBase(int capacity = CircularBuffer::DefaultCapacity) : CircularBuffer(capacity), RunningTotal() { this->Clear(); }; // The following methods are augmented to update the cached running sum value void PushBack(const T &e) { CircularBuffer::PushBack(e); RunningTotal += e; if (this->End == 0) { // update the cached total to avoid error accumulation RunningTotal = T(); for (int i = 0; i < this->ElemCount; i++) RunningTotal += this->Data[i]; } } void PushFront(const T &e) { CircularBuffer::PushFront(e); RunningTotal += e; if (this->Beginning == 0) { // update the cached total to avoid error accumulation RunningTotal = T(); for (int i = 0; i < this->ElemCount; i++) RunningTotal += this->Data[i]; } } T PopBack() { T e = CircularBuffer::PopBack(); RunningTotal -= e; return e; } T PopFront() { T e = CircularBuffer::PopFront(); RunningTotal -= e; return e; } void Clear() { CircularBuffer::Clear(); RunningTotal = T(); } // Simple statistics T Total() const { return RunningTotal; } T Mean() const { return this->IsEmpty() ? T() : (Total() / (float) this->ElemCount); } T MeanN(int n) const { OVR_ASSERT(n > 0); OVR_ASSERT(this->Capacity >= n); T total = T(); for (int i = 0; i < n; i++) { total += this->PeekBack(i); } return total / n; } // A popular family of smoothing filters and smoothed derivatives T SavitzkyGolaySmooth4() { OVR_ASSERT(this->Capacity >= 4); return this->PeekBack(0)*0.7f + this->PeekBack(1)*0.4f + this->PeekBack(2)*0.1f - this->PeekBack(3)*0.2f; } T SavitzkyGolaySmooth8() const { OVR_ASSERT(this->Capacity >= 8); return this->PeekBack(0)*0.41667f + this->PeekBack(1)*0.33333f + this->PeekBack(2)*0.25f + this->PeekBack(3)*0.16667f + this->PeekBack(4)*0.08333f - this->PeekBack(6)*0.08333f - this->PeekBack(7)*0.16667f; } T SavitzkyGolayDerivative4() const { OVR_ASSERT(this->Capacity >= 4); return this->PeekBack(0)*0.3f + this->PeekBack(1)*0.1f - this->PeekBack(2)*0.1f - this->PeekBack(3)*0.3f; } T SavitzkyGolayDerivative5() const { OVR_ASSERT(this->Capacity >= 5); return this->PeekBack(0)*0.2f + this->PeekBack(1)*0.1f - this->PeekBack(3)*0.1f - this->PeekBack(4)*0.2f; } T SavitzkyGolayDerivative12() const { OVR_ASSERT(this->Capacity >= 12); return this->PeekBack(0)*0.03846f + this->PeekBack(1)*0.03147f + this->PeekBack(2)*0.02448f + this->PeekBack(3)*0.01748f + this->PeekBack(4)*0.01049f + this->PeekBack(5)*0.0035f - this->PeekBack(6)*0.0035f - this->PeekBack(7)*0.01049f - this->PeekBack(8)*0.01748f - this->PeekBack(9)*0.02448f - this->PeekBack(10)*0.03147f - this->PeekBack(11)*0.03846f; } T SavitzkyGolayDerivativeN(int n) const { OVR_ASSERT(this->capacity >= n); int m = (n-1)/2; T result = T(); for (int k = 1; k <= m; k++) { int ind1 = m - k; int ind2 = n - m + k - 1; result += (this->PeekBack(ind1) - this->PeekBack(ind2)) * (float) k; } float coef = 3.0f/(m*(m+1.0f)*(2.0f*m+1.0f)); result = result*coef; return result; } T Median() const { T* copy = (T*) OVR_ALLOC(this->ElemCount * sizeof(T)); T result = Alg::Median(ArrayAdaptor(copy)); OVR_FREE(copy); return result; } }; // This class maintains a buffer of sensor data taken over time and implements // various simple filters, most of which are linear functions of the data history. template class SensorFilter : public SensorFilterBase > { public: SensorFilter(int capacity = SensorFilterBase >::DefaultCapacity) : SensorFilterBase >(capacity) { }; // Simple statistics Vector3 Median() const; Vector3 Variance() const; // The diagonal of covariance matrix Matrix3 Covariance() const; Vector3 PearsonCoefficient() const; }; typedef SensorFilter SensorFilterf; typedef SensorFilter SensorFilterd; // This filter operates on the values that are measured in the body frame and rotate with the device class SensorFilterBodyFrame : public SensorFilterBase { private: // low pass filter gain double gain; // sum of squared norms of the values double runningTotalLengthSq; // cumulative rotation quaternion Quatd Q; // current low pass filter output Vector3d output; // make private so it isn't used by accident // in addition to the normal SensorFilterBase::PushBack, keeps track of running sum of LengthSq // for the purpose of variance computations void PushBack(const Vector3d &e) { runningTotalLengthSq += this->IsFull() ? (e.LengthSq() - this->PeekFront().LengthSq()) : e.LengthSq(); SensorFilterBase::PushBack(e); if (this->End == 0) { // update the cached total to avoid error accumulation runningTotalLengthSq = 0; for (int i = 0; i < this->ElemCount; i++) runningTotalLengthSq += this->Data[i].LengthSq(); } } public: SensorFilterBodyFrame(int capacity = SensorFilterBase::DefaultCapacity) : SensorFilterBase(capacity), gain(2.5), runningTotalLengthSq(0), Q(), output() { }; // return the scalar variance of the filter values (rotated to be in the same frame) double Variance() const { return this->IsEmpty() ? 0 : (runningTotalLengthSq / this->ElemCount - this->Mean().LengthSq()); } // return the scalar standard deviation of the filter values (rotated to be in the same frame) double StdDev() const { return sqrt(Variance()); } // confidence value based on the stddev of the data (between 0.0 and 1.0, more is better) double Confidence() const { return Alg::Clamp(0.48 - 0.1 * log(StdDev()), 0.0, 1.0) * this->ElemCount / this->Capacity; } // add a new element to the filter // takes rotation increment since the last update // in order to rotate the previous value to the current body frame void Update(Vector3d value, double deltaT, Quatd deltaQ = Quatd()) { if (this->IsEmpty()) { output = value; } else { // rotate by deltaQ output = deltaQ.Inverted().Rotate(output); // apply low-pass filter output += (value - output) * gain * deltaT; } // put the value into the fixed frame for the stddev computation Q = Q * deltaQ; PushBack(Q.Rotate(output)); } // returns the filter average in the current body frame Vector3d GetFilteredValue() const { return Q.Inverted().Rotate(this->Mean()); } }; } //namespace OVR #endif // OVR_SensorFilter_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusion.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusion.cpp new file mode 100644 index 0000000..cca1ded --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusion.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorFusion.cpp Content : Methods that determine head orientation from sensor data over time Created : October 9, 2012 Authors : Michael Antonov, Steve LaValle, Dov Katz, Max Katsev, Dan Gierl Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_SensorFusion.h" #include "Kernel/OVR_Log.h" #include "Kernel/OVR_System.h" #include "OVR_JSON.h" #include "OVR_Profile.h" #include "OVR_Stereo.h" #include "OVR_Recording.h" // Temporary for debugging bool Global_Flag_1 = true; //Convenient global variables to temporarily extract this data. float TPH_CameraPoseOrientationWxyz[4]; double TPH_CameraPoseConfidence; double TPH_CameraPoseConfidenceThresholdOverrideIfNonZero = 0; bool TPH_IsPositionTracked = false; namespace OVR { const Transformd DefaultWorldFromCamera(Quatd(), Vector3d(0, 0, -1)); //------------------------------------------------------------------------------------- // ***** Sensor Fusion SensorFusion::SensorFusion(SensorDevice* sensor) : ExposureRecordHistory(100), LastMessageExposureFrame(NULL), FocusDirection(Vector3d(0, 0, 0)), FocusFOV(0.0), FAccelInImuFrame(1000), FAccelInCameraFrame(1000), FAngV(20), EnableGravity(true), EnableYawCorrection(true), MagCalibrated(false), EnableCameraTiltCorrection(true), MotionTrackingEnabled(true), VisionPositionEnabled(true), CenterPupilDepth(0.0) { pHandler = new BodyFrameHandler(this); // And the clock is running... LogText("*** SensorFusion Startup: TimeSeconds = %f\n", Timer::GetSeconds()); if (sensor) AttachToSensor(sensor); Reset(); } SensorFusion::~SensorFusion() { delete(pHandler); } bool SensorFusion::AttachToSensor(SensorDevice* sensor) { pHandler->RemoveHandlerFromDevices(); Reset(); if (sensor != NULL) { // cache mag calibration state MagCalibrated = sensor->IsMagCalibrated(); // Load IMU position Array reports; bool result = sensor->GetAllPositionCalibrationReports(&reports); if (result) { PositionCalibrationReport imu = reports[reports.GetSize() - 1]; OVR_ASSERT(imu.PositionType == PositionCalibrationReport::PositionType_IMU); // convert from vision to the world frame // TBD convert rotation as necessary? imu.Position.x *= -1.0; imu.Position.z *= -1.0; ImuFromScreen = Transformd(Quatd(imu.Normal, imu.Angle), imu.Position).Inverted(); Recording::GetRecorder().RecordLedPositions(reports); Recording::GetRecorder().RecordDeviceIfcVersion(sensor->GetDeviceInterfaceVersion()); } // Repopulate CPFOrigin SetCenterPupilDepth(CenterPupilDepth); // Subscribe to sensor updates sensor->AddMessageHandler(pHandler); // Initialize the sensor state // TBD: This is a hack to avoid a race condition if sensor status is checked immediately // after sensor creation but before any data has flowed through. We should probably // not depend strictly on data flow to determine capabilities like orientation and position // tracking, or else use some sort of synchronous method to wait for data LocklessState init; init.StatusFlags = Status_OrientationTracked; UpdatedState.SetState(init); } return true; } // Resets the current orientation void SensorFusion::Reset() { Lock::Locker lockScope(pHandler->GetHandlerLock()); UpdatedState.SetState(LocklessState()); WorldFromImu = PoseState(); WorldFromImu.Pose = ImuFromCpf.Inverted(); // place CPF at the origin, not the IMU CameraFromImu = PoseState(); VisionError = PoseState(); WorldFromCamera = DefaultWorldFromCamera; WorldFromCameraConfidence = -1; ExposureRecordHistory.Clear(); NextExposureRecord = ExposureRecord(); LastMessageExposureFrame = MessageExposureFrame(NULL); LastVisionAbsoluteTime = 0; Stage = 0; MagRefs.Clear(); MagRefIdx = -1; MagCorrectionIntegralTerm = Quatd(); AccelOffset = Vector3d(); FAccelInCameraFrame.Clear(); FAccelInImuFrame.Clear(); FAngV.Clear(); setNeckPivotFromPose ( WorldFromImu.Pose ); } //------------------------------------------------------------------------------------- // Vision & message processing void SensorFusion::OnVisionFailure() { // do nothing Recording::GetRecorder().RecordVisionSuccess(false); } void SensorFusion::OnVisionPreviousFrame(const Transform& cameraFromImu) { // simply save the observation for use in the next OnVisionSuccess call; // this should not have unintended side-effects for position filtering, // since the vision time is not updated and the system keeps thinking we don't have vision yet CameraFromImu.Pose = cameraFromImu; } void SensorFusion::OnVisionSuccess(const Transform& cameraFromImu, UInt32 exposureCounter) { Lock::Locker lockScope(pHandler->GetHandlerLock()); Recording::GetRecorder().RecordVisionSuccess(true); LastVisionAbsoluteTime = GetTime(); // ********* LastVisionExposureRecord ********* // Skip old data and use the record that matches the exposure counter while (!ExposureRecordHistory.IsEmpty() && (ExposureRecordHistory.PeekFront().ExposureCounter <= exposureCounter)) { LastVisionExposureRecord = ExposureRecordHistory.PopFront(); } // Use current values if we don't have historical data // Right now, this will happen if we get first frame after prediction failure, // and this exposure wasn't in the buffer. (TBD: Unlikely.. unless IMU message wasn't sent?) if (LastVisionExposureRecord.ExposureCounter != exposureCounter) LastVisionExposureRecord = ExposureRecord(exposureCounter, GetTime(), WorldFromImu, PoseState()); // ********* CameraFromImu ********* // This is stored in the camera frame, so need to be careful when combining with the IMU data, // which is in the world frame Transformd cameraFromImuPrev = CameraFromImu.Pose; CameraFromImu.Pose = cameraFromImu; CameraFromImu.TimeInSeconds = LastVisionExposureRecord.ExposureTime; // Check LastVisionExposureRecord.Delta.TimeInSeconds to avoid divide by zero, which we could (rarely) // get if we didn't have exposures delta for history (skipped exposure counters // due to video mode change that stalls USB, etc). if (LastVisionExposureRecord.ImuOnlyDelta.TimeInSeconds > 0.001) { Vector3d visionVelocityInImuFrame = (cameraFromImu.Translation - cameraFromImuPrev.Translation) / LastVisionExposureRecord.ImuOnlyDelta.TimeInSeconds; // Use the accel data to estimate the velocity at the exposure time // (as opposed to the average velocity between exposures) Vector3d imuVelocityInWorldFrame = LastVisionExposureRecord.ImuOnlyDelta.LinearVelocity - LastVisionExposureRecord.ImuOnlyDelta.Pose.Translation / LastVisionExposureRecord.ImuOnlyDelta.TimeInSeconds; CameraFromImu.LinearVelocity = visionVelocityInImuFrame + WorldFromCamera.Inverted().Rotate(imuVelocityInWorldFrame); } else { CameraFromImu.LinearVelocity = Vector3d(0,0,0); } } PoseStated SensorFusion::computeVisionError() { PoseStated worldFromImuVision = WorldFromCamera * CameraFromImu; // Here we need to compute the difference between worldFromImuVision and WorldFromImu. // However this difference needs to be represented in the World frame, not IMU frame. // Therefore the computation is different from simply worldFromImuVision.Pose * WorldFromImu.Pose.Inverted(). PoseStated err; err.Pose.Rotation = worldFromImuVision.Pose.Rotation * LastVisionExposureRecord.WorldFromImu.Pose.Rotation.Inverted(); err.Pose.Translation = worldFromImuVision.Pose.Translation - LastVisionExposureRecord.WorldFromImu.Pose.Translation; err.LinearVelocity = worldFromImuVision.LinearVelocity - LastVisionExposureRecord.WorldFromImu.LinearVelocity; return err; } Transform SensorFusion::GetVisionPrediction(UInt32 exposureCounter) { Lock::Locker lockScope(pHandler->GetHandlerLock()); // Combine the small deltas together // Should only be one iteration, unless we are skipping camera frames ExposureRecord record; PoseState delta = PoseState(); while (!ExposureRecordHistory.IsEmpty() && (ExposureRecordHistory.PeekFront().ExposureCounter <= exposureCounter)) { record = ExposureRecordHistory.PopFront(); delta.AdvanceByDelta(record.ImuOnlyDelta); } // Put the combine exposure record back in the history, for use in HandleVisionSuccess(...) record.ImuOnlyDelta = delta; ExposureRecordHistory.PushFront(record); Transformd result; if (record.VisionTrackingAvailable) { // if the tracking is working normally, use the change in the main state (SFusion output) // to compute the prediction result = CameraFromImu.Pose * LastVisionExposureRecord.WorldFromImu.Pose.Inverted() * record.WorldFromImu.Pose; } else { // if we just acquired vision, the main state probably doesn't have the correct position, // so can't rely on it for prediction // solution: use the accelerometer and vision velocity to propagate the previous sample forward // (don't forget to transform IMU to the camera frame) result = Transform ( CameraFromImu.Pose.Rotation * delta.Pose.Rotation, CameraFromImu.Pose.Translation + CameraFromImu.LinearVelocity * delta.TimeInSeconds + WorldFromCamera.Inverted().Rotate(delta.Pose.Translation) ); } return result; } void SensorFusion::handleMessage(const MessageBodyFrame& msg) { if (msg.Type != Message_BodyFrame || !IsMotionTrackingEnabled()) return; // Put the sensor readings into convenient local variables Vector3d gyro(msg.RotationRate); Vector3d accel(msg.Acceleration); Vector3d mag(msg.MagneticField); double DeltaT = msg.TimeDelta; // Keep track of time WorldFromImu.TimeInSeconds = msg.AbsoluteTimeSeconds; // We got an update in the last 60ms and the data is not very old bool visionIsRecent = (GetTime() - LastVisionAbsoluteTime < 0.07) && (GetVisionLatency() < 0.25); Stage++; // Insert current sensor data into filter history FAngV.PushBack(gyro); FAccelInImuFrame.Update(accel, DeltaT, Quatd(gyro, gyro.Length() * DeltaT)); // Process raw inputs // in the future the gravity offset can be calibrated using vision feedback Vector3d accelInWorldFrame = WorldFromImu.Pose.Rotate(accel) - Vector3d(0, 9.8, 0); // Recompute the vision error to account for all the corrections and the new data VisionError = computeVisionError(); // Update headset orientation WorldFromImu.StoreAndIntegrateGyro(gyro, DeltaT); // Tilt correction based on accelerometer if (EnableGravity) applyTiltCorrection(DeltaT); // Yaw correction based on camera if (EnableYawCorrection && visionIsRecent) applyVisionYawCorrection(DeltaT); // Yaw correction based on magnetometer if (EnableYawCorrection && MagCalibrated) // MagCalibrated is always false for DK2 for now applyMagYawCorrection(mag, DeltaT); // Focus Correction if ((FocusDirection.x != 0.0f || FocusDirection.z != 0.0f) && FocusFOV < Mathf::Pi) applyFocusCorrection(DeltaT); // Update camera orientation if (EnableCameraTiltCorrection && visionIsRecent) applyCameraTiltCorrection(accel, DeltaT); // The quaternion magnitude may slowly drift due to numerical error, // so it is periodically normalized. if ((Stage & 0xFF) == 0) { WorldFromImu.Pose.Rotation.Normalize(); WorldFromCamera.Rotation.Normalize(); } // Update headset position if (VisionPositionEnabled && visionIsRecent) { // Integrate UMI and velocity here up to a fixed amount of time after vision. WorldFromImu.StoreAndIntegrateAccelerometer(accelInWorldFrame + AccelOffset, DeltaT); // Position correction based on camera applyPositionCorrection(DeltaT); // Compute where the neck pivot would be. setNeckPivotFromPose(WorldFromImu.Pose); } else { // Fall back onto internal head model // Use the last-known neck pivot position to figure out the expected IMU position. // (should be the opposite of SensorFusion::setNeckPivotFromPose) WorldFromNeck.Rotation = WorldFromImu.Pose.Rotation; WorldFromImu.Pose = WorldFromNeck * (ImuFromCpf * CpfFromNeck).Inverted(); // We can't trust velocity past this point. WorldFromImu.LinearVelocity = Vector3d(0,0,0); WorldFromImu.LinearAcceleration = accelInWorldFrame; } // Compute the angular acceleration WorldFromImu.AngularAcceleration = (FAngV.GetSize() >= 12 && DeltaT > 0) ? (FAngV.SavitzkyGolayDerivative12() / DeltaT) : Vector3d(); // Update the dead reckoning state used for incremental vision tracking NextExposureRecord.ImuOnlyDelta.StoreAndIntegrateGyro(gyro, DeltaT); NextExposureRecord.ImuOnlyDelta.StoreAndIntegrateAccelerometer(accelInWorldFrame, DeltaT); NextExposureRecord.ImuOnlyDelta.TimeInSeconds = WorldFromImu.TimeInSeconds - LastMessageExposureFrame.CameraTimeSeconds; NextExposureRecord.VisionTrackingAvailable &= (VisionPositionEnabled && visionIsRecent); Recording::GetRecorder().LogData("sfTimeSeconds", WorldFromImu.TimeInSeconds); Recording::GetRecorder().LogData("sfStage", (double)Stage); Recording::GetRecorder().LogData("sfPose", WorldFromImu.Pose); //Recorder::LogData("sfAngAcc", State.AngularAcceleration); //Recorder::LogData("sfAngVel", State.AngularVelocity); //Recorder::LogData("sfLinAcc", State.LinearAcceleration); //Recorder::LogData("sfLinVel", State.LinearVelocity); // Store the lockless state. LocklessState lstate; lstate.StatusFlags = Status_OrientationTracked; if (VisionPositionEnabled) lstate.StatusFlags |= Status_PositionConnected; if (VisionPositionEnabled && visionIsRecent) lstate.StatusFlags |= Status_PositionTracked; //A convenient means to temporarily extract this flag TPH_IsPositionTracked = visionIsRecent; lstate.State = WorldFromImu; lstate.Temperature = msg.Temperature; lstate.Magnetometer = mag; UpdatedState.SetState(lstate); } void SensorFusion::handleExposure(const MessageExposureFrame& msg) { NextExposureRecord.ExposureCounter = msg.CameraFrameCount; NextExposureRecord.ExposureTime = msg.CameraTimeSeconds; NextExposureRecord.WorldFromImu = WorldFromImu; NextExposureRecord.ImuOnlyDelta.TimeInSeconds = msg.CameraTimeSeconds - LastMessageExposureFrame.CameraTimeSeconds; ExposureRecordHistory.PushBack(NextExposureRecord); // Every new exposure starts from zero NextExposureRecord = ExposureRecord(); LastMessageExposureFrame = msg; } // If you have a known-good pose, this sets the neck pivot position. void SensorFusion::setNeckPivotFromPose(Transformd const &worldFromImu) { WorldFromNeck = worldFromImu * ImuFromCpf * CpfFromNeck; } // These two functions need to be moved into Quat class // Compute a rotation required to transform "from" into "to". Quatd vectorAlignmentRotation(const Vector3d &from, const Vector3d &to) { Vector3d axis = from.Cross(to); if (axis.LengthSq() == 0) // this handles both collinear and zero-length input cases return Quatd(); double angle = from.Angle(to); return Quatd(axis, angle); } // Compute the part of the quaternion that rotates around Y axis Quatd extractYawRotation(const Quatd &error) { if (error.y == 0) return Quatd(); double phi = atan2(error.w, error.y); double alpha = Mathd::Pi - 2 * phi; return Quatd(Axis_Y, alpha); } void SensorFusion::applyPositionCorrection(double deltaT) { // Each component of gainPos is equivalent to a Kalman gain of (sigma_process / sigma_observation) const Vector3d gainPos = Vector3d(10, 10, 8); const Vector3d gainVel = gainPos.EntrywiseMultiply(gainPos) * 0.5; const Vector3d gainAccel = gainVel * 0.5; const double snapThreshold = 0.1; // Large value (previously 0.01, which caused frequent jumping) Vector3d correctionPos, correctionVel; if (VisionError.Pose.Translation.LengthSq() > (snapThreshold * snapThreshold) || !(UpdatedState.GetState().StatusFlags & Status_PositionTracked)) { // high error or just reacquired position from vision - apply full correction // to know where we are right now, take the vision pose (which is slightly old) // and update it using the imu data since then PoseStated worldFromImuVision = WorldFromCamera * CameraFromImu; for (unsigned int i = 0; i < ExposureRecordHistory.GetSize(); i++) worldFromImuVision.AdvanceByDelta(ExposureRecordHistory.PeekFront(i).ImuOnlyDelta); worldFromImuVision.AdvanceByDelta(NextExposureRecord.ImuOnlyDelta); correctionPos = worldFromImuVision.Pose.Translation - WorldFromImu.Pose.Translation; correctionVel = worldFromImuVision.LinearVelocity - WorldFromImu.LinearVelocity; AccelOffset = Vector3d(); } else { correctionPos = VisionError.Pose.Translation.EntrywiseMultiply(gainPos) * deltaT; correctionVel = VisionError.Pose.Translation.EntrywiseMultiply(gainVel) * deltaT; AccelOffset += VisionError.Pose.Translation.EntrywiseMultiply(gainAccel) * deltaT; } WorldFromImu.Pose.Translation += correctionPos; WorldFromImu.LinearVelocity += correctionVel; // Update the exposure records so that we don't apply the same correction twice LastVisionExposureRecord.WorldFromImu.Pose.Translation += correctionPos; LastVisionExposureRecord.WorldFromImu.LinearVelocity += correctionVel; for (unsigned int i = 0; i < ExposureRecordHistory.GetSize(); i++) { PoseStated& state = ExposureRecordHistory.PeekBack(i).WorldFromImu; state.Pose.Translation += correctionPos; state.LinearVelocity += correctionVel; } } void SensorFusion::applyVisionYawCorrection(double deltaT) { const double gain = 0.25; const double snapThreshold = 0.1; Quatd yawError = extractYawRotation(VisionError.Pose.Rotation); Quatd correction; if (Alg::Abs(yawError.w) < cos(snapThreshold / 2)) // angle(yawError) > snapThreshold // high error, jump to the vision position correction = yawError; else correction = yawError.Nlerp(Quatd(), gain * deltaT); WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation; // Update the exposure records so that we don't apply the same correction twice LastVisionExposureRecord.WorldFromImu.Pose.Rotation = correction * LastVisionExposureRecord.WorldFromImu.Pose.Rotation; for (unsigned int i = 0; i < ExposureRecordHistory.GetSize(); i++) { PoseStated& state = ExposureRecordHistory.PeekBack(i).WorldFromImu; state.Pose.Rotation = correction * state.Pose.Rotation; } } void SensorFusion::applyMagYawCorrection(Vector3d mag, double deltaT) { const double minMagLengthSq = Mathd::Tolerance; // need to use a real value to discard very weak fields const double maxMagRefDist = 0.1; const double maxTiltError = 0.05; const double proportionalGain = 0.01; const double integralGain = 0.0005; Vector3d magInWorldFrame = WorldFromImu.Pose.Rotate(mag); // verify that the horizontal component is sufficient if (magInWorldFrame.x * magInWorldFrame.x + magInWorldFrame.z * magInWorldFrame.z < minMagLengthSq) return; magInWorldFrame.Normalize(); // Delete a bad point if (MagRefIdx >= 0 && MagRefs[MagRefIdx].Score < 0) { MagRefs.RemoveAtUnordered(MagRefIdx); MagRefIdx = -1; } // Update the reference point if needed if (MagRefIdx < 0 || mag.Distance(MagRefs[MagRefIdx].InImuFrame) > maxMagRefDist) { // Find a new one MagRefIdx = -1; double bestDist = maxMagRefDist; for (unsigned int i = 0; i < MagRefs.GetSize(); i++) { double dist = mag.Distance(MagRefs[i].InImuFrame); if (bestDist > dist) { bestDist = dist; MagRefIdx = i; } } // Create one if needed if (MagRefIdx < 0 && MagRefs.GetSize() < MagMaxReferences) { MagRefs.PushBack(MagReferencePoint(mag, WorldFromImu.Pose, 1000)); } } if (MagRefIdx >= 0) { Vector3d magRefInWorldFrame = MagRefs[MagRefIdx].WorldFromImu.Rotate(MagRefs[MagRefIdx].InImuFrame); magRefInWorldFrame.Normalize(); // If the vertical angle is wrong, decrease the score and do nothing if (Alg::Abs(magRefInWorldFrame.y - magInWorldFrame.y) > maxTiltError) { MagRefs[MagRefIdx].Score -= 1; return; } MagRefs[MagRefIdx].Score += 2; #if 0 // this doesn't seem to work properly, need to investigate Quatd error = vectorAlignmentRotation(magW, magRefW); Quatd yawError = extractYawRotation(error); #else // Correction is computed in the horizontal plane magInWorldFrame.y = magRefInWorldFrame.y = 0; Quatd yawError = vectorAlignmentRotation(magInWorldFrame, magRefInWorldFrame); #endif Quatd correction = yawError.Nlerp(Quatd(), proportionalGain * deltaT) * MagCorrectionIntegralTerm.Nlerp(Quatd(), deltaT); MagCorrectionIntegralTerm = MagCorrectionIntegralTerm * yawError.Nlerp(Quatd(), integralGain * deltaT); WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation; } } void SensorFusion::applyTiltCorrection(double deltaT) { const double gain = 0.25; const double snapThreshold = 0.1; const Vector3d up(0, 1, 0); Vector3d accelInWorldFrame = WorldFromImu.Pose.Rotate(FAccelInImuFrame.GetFilteredValue()); Quatd error = vectorAlignmentRotation(accelInWorldFrame, up); Quatd correction; if (FAccelInImuFrame.GetSize() == 1 || ((Alg::Abs(error.w) < cos(snapThreshold / 2) && FAccelInImuFrame.Confidence() > 0.75))) // full correction for start-up // or large error with high confidence correction = error; else if (FAccelInImuFrame.Confidence() > 0.5) correction = error.Nlerp(Quatd(), gain * deltaT); else // accelerometer is unreliable due to movement return; WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation; } void SensorFusion::applyCameraTiltCorrection(Vector3d accel, double deltaT) { const double snapThreshold = 0.02; // in radians const double maxCameraPositionOffset = 0.2; const Vector3d up(0, 1, 0), forward(0, 0, -1); // for startup use filtered value instead of instantaneous for stability if (FAccelInCameraFrame.IsEmpty()) accel = FAccelInImuFrame.GetFilteredValue(); Transformd cameraFromImu = WorldFromCamera.Inverted() * VisionError.Pose * WorldFromImu.Pose; // this is what the hypothetical camera-mounted accelerometer would show Vector3d accelInCameraFrame = cameraFromImu.Rotate(accel); FAccelInCameraFrame.Update(accelInCameraFrame, deltaT); Vector3d cameraAccelInWorldFrame = WorldFromCamera.Rotate(FAccelInCameraFrame.GetFilteredValue()); Quatd error1 = vectorAlignmentRotation(cameraAccelInWorldFrame, up); // cancel out yaw rotation Vector3d forwardCamera = (error1 * WorldFromCamera.Rotation).Rotate(forward); forwardCamera.y = 0; Quatd error2 = vectorAlignmentRotation(forwardCamera, forward); // combined error Quatd error = error2 * error1; double confidence = FAccelInCameraFrame.Confidence(); // penalize the confidence if looking away from the camera // TODO: smooth fall-off if (CameraFromImu.Pose.Rotate(forward).Angle(forward) > 1) confidence *= 0.5; //Convenient global variable to temporarily extract this data. TPH_CameraPoseConfidence = confidence; //Allow override of confidence threshold double confidenceThreshold = 0.75f; if (TPH_CameraPoseConfidenceThresholdOverrideIfNonZero) { confidenceThreshold = TPH_CameraPoseConfidenceThresholdOverrideIfNonZero; } Quatd correction; if (FAccelInCameraFrame.GetSize() == 1 || confidence > WorldFromCameraConfidence + 0.2 || // disabled due to false positives when moving side to side // (Alg::Abs(error.w) < cos(5 * snapThreshold / 2) && confidence > 0.55) || (Alg::Abs(error.w) < cos(snapThreshold / 2) && confidence > confidenceThreshold)) { // large error with high confidence correction = error; // update the confidence level WorldFromCameraConfidence = confidence; } else { // accelerometer is unreliable due to movement return; } Transformd newWorldFromCamera(correction * WorldFromCamera.Rotation, Vector3d()); // compute a camera position change that together with the camera rotation would result in zero player movement newWorldFromCamera.Translation += (WorldFromCamera * CameraFromImu.Pose).Translation - (newWorldFromCamera * CameraFromImu.Pose).Translation; // if the new position is too far, reset to default // (can't hide the rotation, might as well use it to reset the position) if (newWorldFromCamera.Translation.DistanceSq(DefaultWorldFromCamera.Translation) > maxCameraPositionOffset * maxCameraPositionOffset) newWorldFromCamera.Translation = DefaultWorldFromCamera.Translation; WorldFromCamera = newWorldFromCamera; //Convenient global variable to temporarily extract this data. TPH_CameraPoseOrientationWxyz[0] = (float) WorldFromCamera.Rotation.w; TPH_CameraPoseOrientationWxyz[1] = (float) WorldFromCamera.Rotation.x; TPH_CameraPoseOrientationWxyz[2] = (float) WorldFromCamera.Rotation.y; TPH_CameraPoseOrientationWxyz[3] = (float) WorldFromCamera.Rotation.z; } void SensorFusion::applyFocusCorrection(double deltaT) { Vector3d up = Vector3d(0, 1, 0); double gain = 0.01; Vector3d currentDir = WorldFromImu.Pose.Rotate(Vector3d(0, 0, 1)); Vector3d focusYawComponent = FocusDirection.ProjectToPlane(up); Vector3d currentYawComponent = currentDir.ProjectToPlane(up); double angle = focusYawComponent.Angle(currentYawComponent); if( angle > FocusFOV ) { Quatd yawError; if ( FocusFOV != 0.0f) { Vector3d lFocus = Quatd(up, -FocusFOV).Rotate(focusYawComponent); Vector3d rFocus = Quatd(up, FocusFOV).Rotate(focusYawComponent); double lAngle = lFocus.Angle(currentYawComponent); double rAngle = rFocus.Angle(currentYawComponent); if(lAngle < rAngle) { yawError = vectorAlignmentRotation(currentDir, lFocus); } else { yawError = vectorAlignmentRotation(currentDir, rFocus); } } else { yawError = vectorAlignmentRotation(currentYawComponent, focusYawComponent); } Quatd correction = yawError.Nlerp(Quatd(), gain * deltaT); WorldFromImu.Pose.Rotation = correction * WorldFromImu.Pose.Rotation; } } //------------------------------------------------------------------------------------ // Focus filter setting functions void SensorFusion::SetFocusDirection() { SetFocusDirection(WorldFromImu.Pose.Rotate(Vector3d(0.0, 0.0, 1.0))); } void SensorFusion::SetFocusDirection(Vector3d direction) { FocusDirection = direction; } void SensorFusion::SetFocusFOV(double fov) { OVR_ASSERT(fov >= 0.0); FocusFOV = fov; } void SensorFusion::ClearFocus() { FocusDirection = Vector3d(0.0, 0.0, 0.0); FocusFOV = 0.0f; } //------------------------------------------------------------------------------------- // Head model functions. // Sets up head-and-neck model and device-to-pupil dimensions from the user's profile. void SensorFusion::SetUserHeadDimensions(Profile const &profile, HmdRenderInfo const &hmdRenderInfo) { float neckeye[2]; int count = profile.GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2); // Make sure these are vaguely sensible values. if (count == 2) { OVR_ASSERT ( ( neckeye[0] > 0.05f ) && ( neckeye[0] < 0.5f ) ); OVR_ASSERT ( ( neckeye[1] > 0.05f ) && ( neckeye[1] < 0.5f ) ); SetHeadModel ( Vector3f ( 0.0, neckeye[1], -neckeye[0] ) ); } // 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; SetCenterPupilDepth ( centerPupilDepth ); Recording::GetRecorder().RecordUserParams(GetHeadModel(), GetCenterPupilDepth()); } Vector3f SensorFusion::GetHeadModel() const { return (Vector3f)CpfFromNeck.Inverted().Translation; } void SensorFusion::SetHeadModel(const Vector3f &headModel, bool resetNeckPivot /*= true*/ ) { Lock::Locker lockScope(pHandler->GetHandlerLock()); // The head model should look something like (0, 0.12, -0.12), so // these asserts are to try to prevent sign problems, as // they can be subtle but nauseating! OVR_ASSERT ( headModel.y > 0.0f ); OVR_ASSERT ( headModel.z < 0.0f ); CpfFromNeck = Transformd(Quatd(), (Vector3d)headModel).Inverted(); if ( resetNeckPivot ) { setNeckPivotFromPose ( WorldFromImu.Pose ); } } float SensorFusion::GetCenterPupilDepth() const { return CenterPupilDepth; } void SensorFusion::SetCenterPupilDepth(float centerPupilDepth) { CenterPupilDepth = centerPupilDepth; Transformd screenFromCpf(Quatd(), Vector3d(0, 0, centerPupilDepth)); ImuFromCpf = ImuFromScreen * screenFromCpf; setNeckPivotFromPose ( WorldFromImu.Pose ); } //------------------------------------------------------------------------------------- // This is a "perceptually tuned predictive filter", which means that it is optimized // for improvements in the VR experience, rather than pure error. In particular, // jitter is more perceptible at lower speeds whereas latency is more perceptible // after a high-speed motion. Therefore, the prediction interval is dynamically // adjusted based on speed. Significant more research is needed to further improve // this family of filters. static Transform calcPredictedPose(const PoseState& poseState, double predictionDt) { Transform pose = poseState.Pose; const double linearCoef = 1.0; Vector3d angularVelocity = poseState.AngularVelocity; double angularSpeed = angularVelocity.Length(); // This could be tuned so that linear and angular are combined with different coefficients double speed = angularSpeed + linearCoef * poseState.LinearVelocity.Length(); const double slope = 0.2; // The rate at which the dynamic prediction interval varies double candidateDt = slope * speed; // TODO: Replace with smoothstep function double dynamicDt = predictionDt; // Choose the candidate if it is shorter, to improve stability if (candidateDt < predictionDt) dynamicDt = candidateDt; if (angularSpeed > 0.001) pose.Rotation = pose.Rotation * Quatd(angularVelocity, angularSpeed * dynamicDt); pose.Translation += poseState.LinearVelocity * dynamicDt; return pose; } Transformf SensorFusion::GetPoseAtTime(double absoluteTime) const { SensorState ss = GetSensorStateAtTime ( absoluteTime ); return ss.Predicted.Pose; } SensorState SensorFusion::GetSensorStateAtTime(double absoluteTime) const { const LocklessState lstate = UpdatedState.GetState(); // Delta time from the last available data const double pdt = absoluteTime - lstate.State.TimeInSeconds; SensorState ss; ss.Recorded = PoseStatef(lstate.State); ss.Temperature = lstate.Temperature; ss.Magnetometer = Vector3f(lstate.Magnetometer); ss.StatusFlags = lstate.StatusFlags; ss.Predicted = ss.Recorded; ss.Predicted.TimeInSeconds = absoluteTime; // Do prediction logic and ImuFromCpf transformation ss.Recorded.Pose = Transformf(lstate.State.Pose * ImuFromCpf); ss.Predicted.Pose = Transformf(calcPredictedPose(lstate.State, pdt) * ImuFromCpf); return ss; } unsigned SensorFusion::GetStatus() const { return UpdatedState.GetState().StatusFlags; } //------------------------------------------------------------------------------------- void SensorFusion::OnMessage(const MessageBodyFrame& msg) { OVR_ASSERT(!IsAttachedToSensor()); handleMessage(msg); } //------------------------------------------------------------------------------------- void SensorFusion::BodyFrameHandler::OnMessage(const Message& msg) { Recording::GetRecorder().RecordMessage(msg); if (msg.Type == Message_BodyFrame) pFusion->handleMessage(static_cast(msg)); if (msg.Type == Message_ExposureFrame) pFusion->handleExposure(static_cast(msg)); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusion.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusion.h new file mode 100644 index 0000000..690d851 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusion.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_SensorFusion.h Content : Methods that determine head orientation from sensor data over time Created : October 9, 2012 Authors : Michael Antonov, Steve LaValle, Dov Katz, Max Katsev, Dan Gierl Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_SensorFusion_h #define OVR_SensorFusion_h #include "OVR_Device.h" #include "OVR_SensorFilter.h" #include "Kernel/OVR_Timer.h" #include "Kernel/OVR_Threads.h" #include "Kernel/OVR_Lockless.h" // CAPI forward declarations. typedef struct ovrPoseStatef_ ovrPoseStatef; typedef struct ovrSensorState_ ovrSensorState; namespace OVR { struct HmdRenderInfo; //------------------------------------------------------------------------------------- // ***** Sensor State // These values are reported as compatible with C API. // PoseState describes the complete pose, or a rigid body configuration, at a // point in time, including first and second derivatives. It is used to specify // instantaneous location and movement of the headset. // SensorState is returned as a part of the sensor state. template class PoseState { public: typedef typename CompatibleTypes >::Type CompatibleType; PoseState() : TimeInSeconds(0.0) { } // float <-> double conversion constructor. explicit PoseState(const PoseState::OtherFloatType> &src) : Pose(src.Pose), AngularVelocity(src.AngularVelocity), LinearVelocity(src.LinearVelocity), AngularAcceleration(src.AngularAcceleration), LinearAcceleration(src.LinearAcceleration), TimeInSeconds(src.TimeInSeconds) { } // C-interop support: PoseStatef <-> ovrPoseStatef PoseState(const typename CompatibleTypes >::Type& src) : Pose(src.Pose), AngularVelocity(src.AngularVelocity), LinearVelocity(src.LinearVelocity), AngularAcceleration(src.AngularAcceleration), LinearAcceleration(src.LinearAcceleration), TimeInSeconds(src.TimeInSeconds) { } operator typename CompatibleTypes >::Type () const { typename CompatibleTypes >::Type result; result.Pose = Pose; result.AngularVelocity = AngularVelocity; result.LinearVelocity = LinearVelocity; result.AngularAcceleration = AngularAcceleration; result.LinearAcceleration = LinearAcceleration; result.TimeInSeconds = TimeInSeconds; return result; } Transform Pose; Vector3 AngularVelocity; Vector3 LinearVelocity; Vector3 AngularAcceleration; Vector3 LinearAcceleration; // Absolute time of this state sample; always a double measured in seconds. double TimeInSeconds; // ***** Helpers for Pose integration // Stores and integrates gyro angular velocity reading for a given time step. void StoreAndIntegrateGyro(Vector3d angVel, double dt); // Stores and integrates position/velocity from accelerometer reading for a given time step. void StoreAndIntegrateAccelerometer(Vector3d linearAccel, double dt); // Performs integration of state by adding next state delta to it // to produce a combined state change void AdvanceByDelta(const PoseState& delta); }; // External API returns pose as float, but uses doubles internally for quaternion precision. typedef PoseState PoseStatef; typedef PoseState PoseStated; //------------------------------------------------------------------------------------- // ***** Sensor State // Bit flags describing the current status of sensor tracking. enum StatusBits { Status_OrientationTracked = 0x0001, // Orientation is currently tracked (connected and in use). Status_PositionTracked = 0x0002, // Position is currently tracked (false if out of range). Status_PositionConnected = 0x0020, // Position tracking HW is conceded. // Status_HMDConnected = 0x0080 // HMD Display is available & connected. }; // Full state of of the sensor reported by GetSensorState() at a given absolute time. class SensorState { public: SensorState() : Temperature(0), StatusFlags(0) { } // C-interop support SensorState(const ovrSensorState& s); operator ovrSensorState () const; // Pose state at the time that SensorState was requested. PoseStatef Predicted; // Actual recorded pose configuration based on sensor sample at a // moment closest to the requested time. PoseStatef Recorded; // Calibrated magnetometer reading, in Gauss, at sample time. Vector3f Magnetometer; // Sensor temperature reading, in degrees Celsius, at sample time. float Temperature; // Sensor status described by ovrStatusBits. unsigned int StatusFlags; }; //------------------------------------------------------------------------------------- class VisionHandler { public: virtual void OnVisionSuccess(const Transform& cameraFromImu, UInt32 exposureCounter) = 0; virtual void OnVisionPreviousFrame(const Transform& cameraFromImu) = 0; virtual void OnVisionFailure() = 0; // Get a configuration that represents the change over a short time interval virtual Transform GetVisionPrediction(UInt32 exposureCounter) = 0; }; //------------------------------------------------------------------------------------- // ***** SensorFusion // SensorFusion class accumulates Sensor notification messages to keep track of // orientation, which involves integrating the gyro and doing correction with gravity. // Magnetometer based yaw drift correction is also supported; it is usually enabled // automatically based on loaded magnetometer configuration. // Orientation is reported as a quaternion, from which users can obtain either the // rotation matrix or Euler angles. // // The class can operate in two ways: // - By user manually passing MessageBodyFrame messages to the OnMessage() function. // - By attaching SensorFusion to a SensorDevice, in which case it will // automatically handle notifications from that device. class SensorFusion : public NewOverrideBase, public VisionHandler { friend class SensorFusionDebug; enum { MagMaxReferences = 1000 }; public: // ------------------------------------------------------------------------------- // Critical components for tiny API SensorFusion(SensorDevice* sensor = 0); ~SensorFusion(); // Attaches this SensorFusion to the IMU sensor device, from which it will receive // notification messages. If a sensor is attached, manual message notification // is not necessary. Calling this function also resets SensorFusion state. bool AttachToSensor(SensorDevice* sensor); // Returns true if this Sensor fusion object is attached to the IMU. bool IsAttachedToSensor() const; // Sets up head-and-neck model and device-to-pupil dimensions from the user's profile and the HMD stats. // This copes elegantly if profile is NULL. void SetUserHeadDimensions(Profile const &profile, HmdRenderInfo const &hmdRenderInfo); // Get the predicted pose (orientation, position) of the center pupil frame (CPF) at a specific point in time. Transformf GetPoseAtTime(double absoluteTime) const; // Get the full dynamical system state of the CPF, which includes velocities and accelerations, // predicted at a specified absolute point in time. SensorState GetSensorStateAtTime(double absoluteTime) const; // Get the sensor status (same as GetSensorStateAtTime(...).Status) unsigned int GetStatus() const; // End tiny API components // ------------------------------------------------------------------------------- // Resets the current orientation. void Reset (); // Configuration void EnableMotionTracking(bool enable = true) { MotionTrackingEnabled = enable; } bool IsMotionTrackingEnabled() const { return MotionTrackingEnabled; } // Accelerometer/Gravity Correction Control // Enables/disables gravity correction (on by default). void SetGravityEnabled (bool enableGravity); bool IsGravityEnabled () const; // Vision Position and Orientation Configuration // ----------------------------------------------- bool IsVisionPositionEnabled () const; void SetVisionPositionEnabled (bool enableVisionPosition); // compensates for a tilted camera void SetCameraTiltCorrectionEnabled(bool enable); bool IsCameraTiltCorrectionEnabled () const; // Message Handling Logic // ----------------------------------------------- // Notifies SensorFusion object about a new BodyFrame // message from a sensor. // Should be called by user if not attached to sensor. void OnMessage (const MessageBodyFrame& msg); // Interaction with vision // ----------------------------------------------- // Handle observation from vision system (orientation, position, time) virtual void OnVisionSuccess(const Transform& cameraFromImu, UInt32 exposureCounter); virtual void OnVisionPreviousFrame(const Transform& cameraFromImu); virtual void OnVisionFailure(); // Get a configuration that represents the change over a short time interval virtual Transform GetVisionPrediction(UInt32 exposureCounter); double GetTime () const; double GetVisionLatency () const; // Detailed head dimension control // ----------------------------------------------- // These are now deprecated in favour of SetUserHeadDimensions() Vector3f GetHeadModel() const; void SetHeadModel(const Vector3f &headModel, bool resetNeckPivot = true ); float GetCenterPupilDepth() const; void SetCenterPupilDepth(float centerPupilDepth); // Magnetometer and Yaw Drift Section: // --------------------------------------- // Enables/disables magnetometer based yaw drift correction. // Must also have mag calibration data for this correction to work. void SetYawCorrectionEnabled(bool enable); // Determines if yaw correction is enabled. bool IsYawCorrectionEnabled () const; // Clear the reference points associating // mag readings with orientations void ClearMagReferences (); // Sets the focus filter direction to the current HMD direction void SetFocusDirection(); // Sets the focus filter to a direction in the body frame. Once set, a complementary filter // will very slowly drag the world to keep the direction of the HMD within the FOV of the focus void SetFocusDirection(Vector3d direction); // Sets the FOV (in radians) of the focus. When the yaw difference between the HMD's current pose // and the focus is smaller than the FOV, the complementary filter does not act. void SetFocusFOV(double rads); // Turns off the focus filter (equivalent to setting the focus to 0 void ClearFocus(); private: // ----------------------------------------------- class BodyFrameHandler : public NewOverrideBase, public MessageHandler { SensorFusion* pFusion; public: BodyFrameHandler(SensorFusion* fusion) : pFusion(fusion) {} ~BodyFrameHandler(); virtual void OnMessage(const Message& msg); virtual bool SupportsMessageType(MessageType type) const; }; // ----------------------------------------------- // State version stored in lockless updater "queue" and used for // prediction by GetPoseAtTime/GetSensorStateAtTime struct LocklessState { PoseState State; float Temperature; Vector3d Magnetometer; unsigned int StatusFlags; LocklessState() : Temperature(0.0), StatusFlags(0) { }; }; // ----------------------------------------------- // Entry describing the state of the headset at the time of an exposure as reported by the DK2 board. // This is used in combination with the vision data for // incremental tracking based on IMU change and for drift correction struct ExposureRecord { UInt32 ExposureCounter; double ExposureTime; // State of the headset at the time of exposure PoseState WorldFromImu; // Change in state since the last exposure based on IMU data only PoseState ImuOnlyDelta; // Did we have tracking for the entire interval between exposures bool VisionTrackingAvailable; ExposureRecord() : ExposureCounter(0), ExposureTime(0.0), VisionTrackingAvailable(true) { } ExposureRecord(UInt32 exposureCounter, double exposureTime, const PoseState& worldFromImu, const PoseState& imuOnlyDelta) : ExposureCounter(exposureCounter), ExposureTime(exposureTime), WorldFromImu(worldFromImu), ImuOnlyDelta(imuOnlyDelta), VisionTrackingAvailable(true) { } }; // ----------------------------------------------- // Entry describing the magnetometer reference point // Used for mag yaw correction struct MagReferencePoint { Vector3d InImuFrame; Transformd WorldFromImu; int Score; MagReferencePoint() { } MagReferencePoint(const Vector3d& inImuFrame, const Transformd& worldFromImu, int score) : InImuFrame(inImuFrame), WorldFromImu(worldFromImu), Score(score) { } }; // ----------------------------------------------- // The phase of the head as estimated by sensor fusion PoseState WorldFromImu; // State that can be read without any locks, so that high priority rendering thread // doesn't have to worry about being blocked by a sensor/vision threads that got preempted. LocklessUpdater UpdatedState; // The pose we got from Vision, augmented with velocity information from numerical derivatives PoseState CameraFromImu; // Difference between the vision and sensor fusion poses at the time of last exposure adjusted // by all the corrections applied since then // NB: this one is unlike all the other poses/transforms we use, since it's a difference // between 2 WorldFromImu transforms, but is stored in the world frame, not the IMU frame // (see computeVisionError() for details) // For composition purposes it should be considered a WorldFromWorld transform, where the left // side comes from vision and the right - from sensor fusion PoseState VisionError; // Past exposure records between the last update from vision and now // (should only be one record unless vision latency is high) CircularBuffer ExposureRecordHistory; // ExposureRecord that corresponds to the last pose we got from vision ExposureRecord LastVisionExposureRecord; // Incomplete ExposureRecord that will go into the history buffer when // the new MessageExposureFrame is received ExposureRecord NextExposureRecord; // Timings of the previous exposure, used to populate ExposureRecordHistory MessageExposureFrame LastMessageExposureFrame; // Time of the last vision update double LastVisionAbsoluteTime; unsigned int Stage; BodyFrameHandler *pHandler; Vector3d FocusDirection; double FocusFOV; SensorFilterBodyFrame FAccelInImuFrame, FAccelInCameraFrame; SensorFilterd FAngV; Vector3d AccelOffset; bool EnableGravity; bool EnableYawCorrection; bool MagCalibrated; Array MagRefs; int MagRefIdx; Quatd MagCorrectionIntegralTerm; bool EnableCameraTiltCorrection; // Describes the pose of the camera in the world coordinate system Transformd WorldFromCamera; double WorldFromCameraConfidence; bool MotionTrackingEnabled; bool VisionPositionEnabled; // This is a signed distance, but positive because Z increases looking inward. // This is expressed relative to the IMU in the HMD and corresponds to the location // of the cyclopean virtual camera focal point if both the physical and virtual // worlds are isometrically mapped onto each other. -Steve float CenterPupilDepth; // Describes the position of the user eyes relative to the IMU Transformd ImuFromCpf; // Position of the center of the screen relative to the IMU (loaded from the headset firmware) Transformd ImuFromScreen; // Built-in head model for faking position using orientation only Transformd CpfFromNeck; // Last known base of the neck pose used for head model computations Transformd WorldFromNeck; //--------------------------------------------- // Internal handler for messages // bypasses error checking. void handleMessage(const MessageBodyFrame& msg); void handleExposure(const MessageExposureFrame& msg); // Compute the difference between vision and sensor fusion data PoseStated computeVisionError(); // Apply headset yaw correction from magnetometer // for models without camera or when camera isn't available void applyMagYawCorrection(Vector3d mag, double deltaT); // Apply headset tilt correction from the accelerometer void applyTiltCorrection(double deltaT); // Apply headset yaw correction from the camera void applyVisionYawCorrection(double deltaT); // Apply headset position correction from the camera void applyPositionCorrection(double deltaT); // Apply camera tilt correction from the accelerometer void applyCameraTiltCorrection(Vector3d accel, double deltaT); // Apply camera focus correction void applyFocusCorrection(double deltaT); // If you have a known-good pose, this sets the neck pivot position. void setNeckPivotFromPose ( Transformd const &pose ); }; //------------------------------------------------------------------------------------- // ***** SensorFusion - Inlines inline bool SensorFusion::IsAttachedToSensor() const { return pHandler->IsHandlerInstalled(); } inline void SensorFusion::SetGravityEnabled(bool enableGravity) { EnableGravity = enableGravity; } inline bool SensorFusion::IsGravityEnabled() const { return EnableGravity; } inline void SensorFusion::SetYawCorrectionEnabled(bool enable) { EnableYawCorrection = enable; } inline bool SensorFusion::IsYawCorrectionEnabled() const { return EnableYawCorrection; } inline bool SensorFusion::IsVisionPositionEnabled() const { return VisionPositionEnabled; } inline void SensorFusion::SetVisionPositionEnabled(bool enableVisionPosition) { VisionPositionEnabled = enableVisionPosition; } inline void SensorFusion::SetCameraTiltCorrectionEnabled(bool enable) { EnableCameraTiltCorrection = enable; } inline bool SensorFusion::IsCameraTiltCorrectionEnabled() const { return EnableCameraTiltCorrection; } inline double SensorFusion::GetVisionLatency() const { return LastVisionAbsoluteTime - CameraFromImu.TimeInSeconds; } inline double SensorFusion::GetTime() const { return Timer::GetSeconds(); } inline SensorFusion::BodyFrameHandler::~BodyFrameHandler() { RemoveHandlerFromDevices(); } inline bool SensorFusion::BodyFrameHandler::SupportsMessageType(MessageType type) const { return (type == Message_BodyFrame || type == Message_ExposureFrame); } //------------------------------------------------------------------------------------- // ***** PoseState - Inlines // Stores and integrates gyro angular velocity reading for a given time step. template void PoseState::StoreAndIntegrateGyro(Vector3d angVel, double dt) { AngularVelocity = angVel; double angle = angVel.Length() * dt; if (angle > 0) Pose.Rotation = Pose.Rotation * Quatd(angVel, angle); } template void PoseState::StoreAndIntegrateAccelerometer(Vector3d linearAccel, double dt) { LinearAcceleration = linearAccel; Pose.Translation += LinearVelocity * dt + LinearAcceleration * (dt * dt * 0.5); LinearVelocity += LinearAcceleration * dt; } // Performs integration of state by adding next state delta to it // to produce a combined state change template void PoseState::AdvanceByDelta(const PoseState& delta) { Pose.Rotation = Pose.Rotation * delta.Pose.Rotation; Pose.Translation += delta.Pose.Translation + LinearVelocity * delta.TimeInSeconds; LinearVelocity += delta.LinearVelocity; TimeInSeconds += delta.TimeInSeconds; } } // namespace OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusionDebug.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusionDebug.h new file mode 100644 index 0000000..19b8c6b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorFusionDebug.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_SensorFusionDebug.h Content : Friend proxy to allow debugging access to SensorFusion Created : April 16, 2014 Authors : Dan Gierl Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_SensorFusionDebug_h #define OVR_SensorFusionDebug_h #include "OVR_SensorFusion.h" namespace OVR { class SensorFusionDebug { private: SensorFusion * sf; public: SensorFusionDebug (SensorFusion * const sf) : sf(sf) { } // Returns the number of magnetometer reference points currently gathered int GetNumMagRefPoints () const; // Returns the index of the magnetometer reference point being currently used int GetCurMagRefPointIdx () const; // Returns a copy of all the data associated with a magnetometer reference point // This includes it's score, the magnetometer reading as a vector, // and the HMD's pose at the time it was gathered void GetMagRefData (int idx, int * score, Vector3d * magBF, Quatd * magPose) const; }; //------------------------------------------------------------------------------------ // Magnetometer reference point access functions int SensorFusionDebug::GetNumMagRefPoints() const { return (int)sf->MagRefs.GetSize(); } int SensorFusionDebug::GetCurMagRefPointIdx() const { return sf->MagRefIdx; } void SensorFusionDebug::GetMagRefData(int idx, int * score, Vector3d * magBF, Quatd * magPose) const { OVR_ASSERT(idx >= 0 && idx < GetNumMagRefPoints()); *score = sf->MagRefs[idx].Score; *magBF = sf->MagRefs[idx].InImuFrame; *magPose = sf->MagRefs[idx].WorldFromImu.Rotation; } } // OVR #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl.cpp new file mode 100644 index 0000000..5a3a047 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorImpl.cpp Content : Oculus Sensor device implementation. Created : March 7, 2013 Authors : Lee Cooper, Dov Katz Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_SensorImpl.h" #include "OVR_Sensor2Impl.h" #include "OVR_SensorImpl_Common.h" #include "OVR_JSON.h" #include "OVR_Profile.h" #include "Kernel/OVR_Alg.h" #include // HMDDeviceDesc can be created/updated through Sensor carrying DisplayInfo. #include "Kernel/OVR_Timer.h" //extern FILE *SF_LOG_fp; namespace OVR { using namespace Alg; //------------------------------------------------------------------------------------- // ***** Oculus Sensor-specific packet data structures enum { Sensor_VendorId = Oculus_VendorId, Sensor_Tracker_ProductId = Device_Tracker_ProductId, Sensor_Tracker2_ProductId = Device_Tracker2_ProductId, Sensor_KTracker_ProductId = Device_KTracker_ProductId, Sensor_BootLoader = 0x1001, Sensor_DefaultReportRate = 500, // Hz Sensor_MaxReportRate = 1000 // Hz }; // Messages we care for enum TrackerMessageType { TrackerMessage_None = 0, TrackerMessage_Sensors = 1, TrackerMessage_Unknown = 0x100, TrackerMessage_SizeError = 0x101, }; struct TrackerSensors { UByte SampleCount; UInt16 Timestamp; UInt16 LastCommandID; SInt16 Temperature; TrackerSample Samples[3]; SInt16 MagX, MagY, MagZ; TrackerMessageType Decode(const UByte* buffer, int size) { if (size < 62) return TrackerMessage_SizeError; SampleCount = buffer[1]; Timestamp = DecodeUInt16(buffer + 2); LastCommandID = DecodeUInt16(buffer + 4); Temperature = DecodeSInt16(buffer + 6); //if (SampleCount > 2) // OVR_DEBUG_LOG_TEXT(("TackerSensor::Decode SampleCount=%d\n", SampleCount)); // Only unpack as many samples as there actually are int iterationCount = (SampleCount > 2) ? 3 : SampleCount; for (int i = 0; i < iterationCount; i++) { UnpackSensor(buffer + 8 + 16 * i, &Samples[i].AccelX, &Samples[i].AccelY, &Samples[i].AccelZ); UnpackSensor(buffer + 16 + 16 * i, &Samples[i].GyroX, &Samples[i].GyroY, &Samples[i].GyroZ); } MagX = DecodeSInt16(buffer + 56); MagY = DecodeSInt16(buffer + 58); MagZ = DecodeSInt16(buffer + 60); return TrackerMessage_Sensors; } }; struct TrackerMessage { TrackerMessageType Type; TrackerSensors Sensors; }; //------------------------------------------------------------------------------------- // ***** SensorDisplayInfoImpl SensorDisplayInfoImpl::SensorDisplayInfoImpl() : CommandId(0), DistortionType(Base_None) { memset(Buffer, 0, PacketSize); Buffer[0] = 9; } void SensorDisplayInfoImpl::Unpack() { CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); DistortionType = Buffer[3]; HResolution = DecodeUInt16(Buffer+4); VResolution = DecodeUInt16(Buffer+6); HScreenSize = DecodeUInt32(Buffer+8) * (1/1000000.f); VScreenSize = DecodeUInt32(Buffer+12) * (1/1000000.f); VCenter = DecodeUInt32(Buffer+16) * (1/1000000.f); LensSeparation = DecodeUInt32(Buffer+20) * (1/1000000.f); #if 0 // These are not well-measured on most devices - probably best to ignore them. OutsideLensSurfaceToScreen[0] = DecodeUInt32(Buffer+24) * (1/1000000.f); OutsideLensSurfaceToScreen[1] = DecodeUInt32(Buffer+28) * (1/1000000.f); // TODO: add spline-based distortion. // TODO: currently these values are all zeros in the HMD itself. DistortionK[0] = DecodeFloat(Buffer+32); DistortionK[1] = DecodeFloat(Buffer+36); DistortionK[2] = DecodeFloat(Buffer+40); DistortionK[3] = DecodeFloat(Buffer+44); DistortionK[4] = DecodeFloat(Buffer+48); DistortionK[5] = DecodeFloat(Buffer+52); #else // The above are either measured poorly, or don't have values at all. // To remove the temptation to use them, set them to junk. OutsideLensSurfaceToScreen[0] = -1.0f; OutsideLensSurfaceToScreen[1] = -1.0f; DistortionK[0] = -1.0f; DistortionK[1] = -1.0f; DistortionK[2] = -1.0f; DistortionK[3] = -1.0f; DistortionK[4] = -1.0f; DistortionK[5] = -1.0f; #endif } //------------------------------------------------------------------------------------- // ***** SensorDeviceFactory SensorDeviceFactory &SensorDeviceFactory::GetInstance() { static SensorDeviceFactory instance; return instance; } void SensorDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) { class SensorEnumerator : public HIDEnumerateVisitor { // Assign not supported; suppress MSVC warning. void operator = (const SensorEnumerator&) { } DeviceFactory* pFactory; EnumerateVisitor& ExternalVisitor; public: SensorEnumerator(DeviceFactory* factory, EnumerateVisitor& externalVisitor) : pFactory(factory), ExternalVisitor(externalVisitor) { } virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) { return pFactory->MatchVendorProduct(vendorId, productId); } virtual void Visit(HIDDevice& device, const HIDDeviceDesc& desc) { if (desc.ProductId == Sensor_BootLoader) { // If we find a sensor in boot loader mode then notify the app // about the existence of the device, but don't allow the app // to create or access the device BootLoaderDeviceCreateDesc createDesc(pFactory, desc); ExternalVisitor.Visit(createDesc); return; } SensorDeviceCreateDesc createDesc(pFactory, desc); ExternalVisitor.Visit(createDesc); // Check if the sensor returns DisplayInfo. If so, try to use it to override potentially // mismatching monitor information (in case wrong EDID is reported by splitter), // or to create a new "virtualized" HMD Device. SensorDisplayInfoImpl displayInfo; if (device.GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) { displayInfo.Unpack(); // If we got display info, try to match / create HMDDevice as well // so that sensor settings give preference. if (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) { SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo(displayInfo, ExternalVisitor); } } } }; //double start = Timer::GetProfileSeconds(); SensorEnumerator sensorEnumerator(this, visitor); GetManagerImpl()->GetHIDDeviceManager()->Enumerate(&sensorEnumerator); //double totalSeconds = Timer::GetProfileSeconds() - start; } bool SensorDeviceFactory::MatchVendorProduct(UInt16 vendorId, UInt16 productId) const { return ((vendorId == Sensor_VendorId) && (productId == Sensor_Tracker_ProductId)) || ((vendorId == Sensor_VendorId) && (productId == Sensor_Tracker2_ProductId)) || ((vendorId == Sensor_VendorId) && (productId == Sensor_KTracker_ProductId)); } bool SensorDeviceFactory::DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc) { if (MatchVendorProduct(desc.VendorId, desc.ProductId)) { if (desc.ProductId == Sensor_BootLoader) { // If we find a sensor in boot loader mode then notify the app // about the existence of the device, but don't allow them // to create or access the device BootLoaderDeviceCreateDesc createDesc(this, desc); pdevMgr->AddDevice_NeedsLock(createDesc); return false; // return false to allow upstream boot loader factories to catch the device } else { SensorDeviceCreateDesc createDesc(this, desc); return pdevMgr->AddDevice_NeedsLock(createDesc).GetPtr() != NULL; } } return false; } //------------------------------------------------------------------------------------- // ***** SensorDeviceCreateDesc DeviceBase* SensorDeviceCreateDesc::NewDeviceInstance() { if (HIDDesc.ProductId == Sensor_Tracker2_ProductId) { return new Sensor2DeviceImpl(this); } return new SensorDeviceImpl(this); } bool SensorDeviceCreateDesc::GetDeviceInfo(DeviceInfo* info) const { if ((info->InfoClassType != Device_Sensor) && (info->InfoClassType != Device_None)) return false; info->Type = Device_Sensor; info->ProductName = HIDDesc.Product; info->Manufacturer = HIDDesc.Manufacturer; info->Version = HIDDesc.VersionNumber; if (info->InfoClassType == Device_Sensor) { SensorInfo* sinfo = (SensorInfo*)info; sinfo->VendorId = HIDDesc.VendorId; sinfo->ProductId = HIDDesc.ProductId; sinfo->MaxRanges = SensorRangeImpl::GetMaxSensorRange(); sinfo->SerialNumber = HIDDesc.SerialNumber; } return true; } //------------------------------------------------------------------------------------- // ***** SensorDevice SensorDeviceImpl::SensorDeviceImpl(SensorDeviceCreateDesc* createDesc) : OVR::HIDDeviceImpl(createDesc, 0), Coordinates(SensorDevice::Coord_Sensor), HWCoordinates(SensorDevice::Coord_HMD), // HW reports HMD coordinates by default. NextKeepAliveTickSeconds(0), FullTimestamp(0), MaxValidRange(SensorRangeImpl::GetMaxSensorRange()), magCalibrated(false) { SequenceValid = false; LastSampleCount = 0; LastTimestamp = 0; OldCommandId = 0; PrevAbsoluteTime = 0.0; #ifdef OVR_OS_ANDROID pPhoneSensors = PhoneSensors::Create(); #endif } SensorDeviceImpl::~SensorDeviceImpl() { // Check that Shutdown() was called. OVR_ASSERT(!pCreateDesc->pDevice); } // Internal creation APIs. bool SensorDeviceImpl::Initialize(DeviceBase* parent) { if (HIDDeviceImpl::Initialize(parent)) { openDevice(); return true; } return false; } void SensorDeviceImpl::openDevice() { // Read the currently configured range from sensor. SensorRangeImpl sr(SensorRange(), 0); if (GetInternalDevice()->GetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) { sr.Unpack(); sr.GetSensorRange(&CurrentRange); // Increase the magnetometer range, since the default value is not enough in practice CurrentRange.MaxMagneticField = 2.5f; setRange(CurrentRange); } // Read the currently configured calibration from sensor. SensorFactoryCalibrationImpl sc; if (GetInternalDevice()->GetFeatureReport(sc.Buffer, SensorFactoryCalibrationImpl::PacketSize)) { sc.Unpack(); AccelCalibrationOffset = sc.AccelOffset; GyroCalibrationOffset = sc.GyroOffset; AccelCalibrationMatrix = sc.AccelMatrix; GyroCalibrationMatrix = sc.GyroMatrix; CalibrationTemperature = sc.Temperature; } // If the sensor has "DisplayInfo" data, use HMD coordinate frame by default. SensorDisplayInfoImpl displayInfo; if (GetInternalDevice()->GetFeatureReport(displayInfo.Buffer, SensorDisplayInfoImpl::PacketSize)) { displayInfo.Unpack(); Coordinates = (displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) ? Coord_HMD : Coord_Sensor; } // Read/Apply sensor config. setCoordinateFrame(Coordinates); setReportRate(Sensor_DefaultReportRate); // Set Keep-alive at 10 seconds. SensorKeepAliveImpl skeepAlive(10 * 1000); GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize); // Load mag calibration MagCalibrationReport report; bool res = GetMagCalibrationReport(&report); if (res && report.Version > 0) { magCalibration = report.Calibration; magCalibrated = true; } } void SensorDeviceImpl::closeDeviceOnError() { LogText("OVR::SensorDevice - Lost connection to '%s'\n", getHIDDesc()->Path.ToCStr()); NextKeepAliveTickSeconds = 0; } void SensorDeviceImpl::Shutdown() { HIDDeviceImpl::Shutdown(); LogText("OVR::SensorDevice - Closed '%s'\n", getHIDDesc()->Path.ToCStr()); } void SensorDeviceImpl::OnInputReport(UByte* pData, UInt32 length) { bool processed = false; if (!processed) { TrackerMessage message; if (decodeTrackerMessage(&message, pData, length)) { processed = true; onTrackerMessage(&message); } } } double SensorDeviceImpl::OnTicks(double tickSeconds) { if (tickSeconds >= NextKeepAliveTickSeconds) { // Use 3-seconds keep alive by default. double keepAliveDelta = 3.0; // Set Keep-alive at 10 seconds. SensorKeepAliveImpl skeepAlive(10 * 1000); // OnTicks is called from background thread so we don't need to add this to the command queue. GetInternalDevice()->SetFeatureReport(skeepAlive.Buffer, SensorKeepAliveImpl::PacketSize); // Emit keep-alive every few seconds. NextKeepAliveTickSeconds = tickSeconds + keepAliveDelta; } return NextKeepAliveTickSeconds - tickSeconds; } bool SensorDeviceImpl::SetRange(const SensorRange& range, bool waitFlag) { bool result = 0; ThreadCommandQueue * threadQueue = GetManagerImpl()->GetThreadQueue(); if (!waitFlag) { return threadQueue->PushCall(this, &SensorDeviceImpl::setRange, range); } if (!threadQueue->PushCallAndWaitResult(this, &SensorDeviceImpl::setRange, &result, range)) { return false; } return result; } void SensorDeviceImpl::GetRange(SensorRange* range) const { Lock::Locker lockScope(GetLock()); *range = CurrentRange; } bool SensorDeviceImpl::setRange(const SensorRange& range) { SensorRangeImpl sr(range); if (GetInternalDevice()->SetFeatureReport(sr.Buffer, SensorRangeImpl::PacketSize)) { Lock::Locker lockScope(GetLock()); sr.GetSensorRange(&CurrentRange); return true; } return false; } void SensorDeviceImpl::SetCoordinateFrame(CoordinateFrame coordframe) { // Push call with wait. GetManagerImpl()->GetThreadQueue()-> PushCall(this, &SensorDeviceImpl::setCoordinateFrame, coordframe, true); } SensorDevice::CoordinateFrame SensorDeviceImpl::GetCoordinateFrame() const { return Coordinates; } Void SensorDeviceImpl::setCoordinateFrame(CoordinateFrame coordframe) { Coordinates = coordframe; // Read the original coordinate frame, then try to change it. SensorConfigImpl scfg; if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) { scfg.Unpack(); } scfg.SetSensorCoordinates(coordframe == Coord_Sensor); scfg.Pack(); GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); // Re-read the state, in case of older firmware that doesn't support Sensor coordinates. if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) { scfg.Unpack(); HWCoordinates = scfg.IsUsingSensorCoordinates() ? Coord_Sensor : Coord_HMD; } else { HWCoordinates = Coord_HMD; } return 0; } void SensorDeviceImpl::SetReportRate(unsigned rateHz) { // Push call with wait. GetManagerImpl()->GetThreadQueue()-> PushCall(this, &SensorDeviceImpl::setReportRate, rateHz, true); } unsigned SensorDeviceImpl::GetReportRate() const { // Read the original configuration SensorConfigImpl scfg; if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) { scfg.Unpack(); return Sensor_MaxReportRate / (scfg.PacketInterval + 1); } return 0; // error } Void SensorDeviceImpl::setReportRate(unsigned rateHz) { // Read the original configuration SensorConfigImpl scfg; if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) { scfg.Unpack(); } if (rateHz > Sensor_MaxReportRate) rateHz = Sensor_MaxReportRate; else if (rateHz == 0) rateHz = Sensor_DefaultReportRate; scfg.PacketInterval = UInt16((Sensor_MaxReportRate / rateHz) - 1); scfg.Pack(); GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); return 0; } void SensorDeviceImpl::GetFactoryCalibration(Vector3f* AccelOffset, Vector3f* GyroOffset, Matrix4f* AccelMatrix, Matrix4f* GyroMatrix, float* Temperature) { *AccelOffset = AccelCalibrationOffset; *GyroOffset = GyroCalibrationOffset; *AccelMatrix = AccelCalibrationMatrix; *GyroMatrix = GyroCalibrationMatrix; *Temperature = CalibrationTemperature; } bool SensorDeviceImpl::IsMagCalibrated() { return magCalibrated; } void SensorDeviceImpl::SetOnboardCalibrationEnabled(bool enabled) { // Push call with wait. GetManagerImpl()->GetThreadQueue()-> PushCall(this, &SensorDeviceImpl::setOnboardCalibrationEnabled, enabled, true); } Void SensorDeviceImpl::setOnboardCalibrationEnabled(bool enabled) { // Read the original configuration SensorConfigImpl scfg; if (GetInternalDevice()->GetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize)) { scfg.Unpack(); } if (enabled) scfg.Flags |= (SensorConfigImpl::Flag_AutoCalibration | SensorConfigImpl::Flag_UseCalibration); else scfg.Flags &= ~(SensorConfigImpl::Flag_AutoCalibration | SensorConfigImpl::Flag_UseCalibration); scfg.Pack(); GetInternalDevice()->SetFeatureReport(scfg.Buffer, SensorConfigImpl::PacketSize); return 0; } void SensorDeviceImpl::AddMessageHandler(MessageHandler* handler) { if (handler) SequenceValid = false; DeviceBase::AddMessageHandler(handler); } // Sensor reports data in the following coordinate system: // Accelerometer: 10^-4 m/s^2; X forward, Y right, Z Down. // Gyro: 10^-4 rad/s; X positive roll right, Y positive pitch up; Z positive yaw right. // We need to convert it to the following RHS coordinate system: // X right, Y Up, Z Back (out of screen) // Vector3f AccelFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber, bool convertHMDToSensor = false) { const TrackerSample& sample = update.Samples[sampleNumber]; float ax = (float)sample.AccelX; float ay = (float)sample.AccelY; float az = (float)sample.AccelZ; Vector3f val = convertHMDToSensor ? Vector3f(ax, az, -ay) : Vector3f(ax, ay, az); return val * 0.0001f; } Vector3f MagFromBodyFrameUpdate(const TrackerSensors& update, Matrix4f magCalibration, bool convertHMDToSensor = false) { float mx = (float)update.MagX; float my = (float)update.MagY; float mz = (float)update.MagZ; // Note: Y and Z are swapped in comparison to the Accel. // This accounts for DK1 sensor firmware axis swap, which should be undone in future releases. Vector3f mag = convertHMDToSensor ? Vector3f(mx, my, -mz) : Vector3f(mx, mz, my); mag *= 0.0001f; // Apply calibration return magCalibration.Transform(mag); } Vector3f EulerFromBodyFrameUpdate(const TrackerSensors& update, UByte sampleNumber, bool convertHMDToSensor = false) { const TrackerSample& sample = update.Samples[sampleNumber]; float gx = (float)sample.GyroX; float gy = (float)sample.GyroY; float gz = (float)sample.GyroZ; Vector3f val = convertHMDToSensor ? Vector3f(gx, gz, -gy) : Vector3f(gx, gy, gz); return val * 0.0001f; } bool SensorDeviceImpl::decodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size) { memset(message, 0, sizeof(TrackerMessage)); if (size < 4) { message->Type = TrackerMessage_SizeError; return false; } switch (buffer[0]) { case TrackerMessage_Sensors: message->Type = message->Sensors.Decode(buffer, size); break; default: message->Type = TrackerMessage_Unknown; break; } return (message->Type < TrackerMessage_Unknown) && (message->Type != TrackerMessage_None); } void SensorDeviceImpl::onTrackerMessage(TrackerMessage* message) { if (message->Type != TrackerMessage_Sensors) return; const double timeUnit = (1.0 / 1000.0); double scaledTimeUnit = timeUnit; TrackerSensors& s = message->Sensors; // DK1 timestamps the first sample, so the actual device time will be later // by the time we get the message if there are multiple samples. int timestampAdjust = (s.SampleCount > 0) ? s.SampleCount-1 : 0; const double now = Timer::GetSeconds(); double absoluteTimeSeconds = 0.0; if (SequenceValid) { unsigned timestampDelta; if (s.Timestamp < LastTimestamp) { // The timestamp rolled around the 16 bit counter, so FullTimeStamp // needs a high word increment. FullTimestamp += 0x10000; timestampDelta = ((((int)s.Timestamp) + 0x10000) - (int)LastTimestamp); } else { timestampDelta = (s.Timestamp - LastTimestamp); } // Update the low word of FullTimeStamp FullTimestamp = ( FullTimestamp & ~0xffff ) | s.Timestamp; double deviceTime = (FullTimestamp + timestampAdjust) * timeUnit; absoluteTimeSeconds = TimeFilter.SampleToSystemTime(deviceTime, now, PrevAbsoluteTime); scaledTimeUnit = TimeFilter.ScaleTimeUnit(timeUnit); PrevAbsoluteTime = absoluteTimeSeconds; // If we missed a small number of samples, generate the sample that would have immediately // proceeded the current one. Re-use the IMU values from the last processed sample. if ((timestampDelta > LastSampleCount) && (timestampDelta <= 254)) { if (HandlerRef.HasHandlers()) { MessageBodyFrame sensors(this); sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - s.SampleCount * scaledTimeUnit; sensors.TimeDelta = (float)((timestampDelta - LastSampleCount) * scaledTimeUnit); sensors.Acceleration = LastAcceleration; sensors.RotationRate = LastRotationRate; sensors.MagneticField = LastMagneticField; sensors.Temperature = LastTemperature; HandlerRef.Call(sensors); } } } else { LastAcceleration = Vector3f(0); LastRotationRate = Vector3f(0); LastMagneticField= Vector3f(0); LastTemperature = 0; SequenceValid = true; // This is our baseline sensor to host time delta, // it will be adjusted with each new message. FullTimestamp = s.Timestamp; double deviceTime = (FullTimestamp + timestampAdjust) * timeUnit; absoluteTimeSeconds = TimeFilter.SampleToSystemTime(deviceTime, now, PrevAbsoluteTime); scaledTimeUnit = TimeFilter.ScaleTimeUnit(timeUnit); PrevAbsoluteTime = absoluteTimeSeconds; } LastSampleCount = s.SampleCount; LastTimestamp = s.Timestamp; bool convertHMDToSensor = (Coordinates == Coord_Sensor) && (HWCoordinates == Coord_HMD); #ifdef OVR_OS_ANDROID // LDC - Normally we get the coordinate system from the tracker. // Since KTracker doesn't store it we'll always assume HMD coordinate system. convertHMDToSensor = false; #endif if (HandlerRef.HasHandlers()) { MessageBodyFrame sensors(this); UByte iterations = s.SampleCount; if (s.SampleCount > 3) { iterations = 3; sensors.TimeDelta = (float)((s.SampleCount - 2) * scaledTimeUnit); } else { sensors.TimeDelta = (float)scaledTimeUnit; } for (UByte i = 0; i < iterations; i++) { sensors.AbsoluteTimeSeconds = absoluteTimeSeconds - ( iterations - 1 - i ) * scaledTimeUnit; sensors.Acceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor); sensors.RotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor); sensors.MagneticField = MagFromBodyFrameUpdate(s, magCalibration, convertHMDToSensor); #ifdef OVR_OS_ANDROID replaceWithPhoneMag(&(sensors.MagneticField)); #endif sensors.Temperature = s.Temperature * 0.01f; HandlerRef.Call(sensors); // TimeDelta for the last two sample is always fixed. sensors.TimeDelta = (float)scaledTimeUnit; } LastAcceleration = sensors.Acceleration; LastRotationRate = sensors.RotationRate; LastMagneticField= sensors.MagneticField; LastTemperature = sensors.Temperature; } else { UByte i = (s.SampleCount > 3) ? 2 : (s.SampleCount - 1); LastAcceleration = AccelFromBodyFrameUpdate(s, i, convertHMDToSensor); LastRotationRate = EulerFromBodyFrameUpdate(s, i, convertHMDToSensor); LastMagneticField = MagFromBodyFrameUpdate(s, magCalibration, convertHMDToSensor); #ifdef OVR_OS_ANDROID replaceWithPhoneMag(&LastMagneticField); #endif LastTemperature = s.Temperature * 0.01f; } } #ifdef OVR_OS_ANDROID void SensorDeviceImpl::replaceWithPhoneMag(Vector3f* val) { // Native calibrated. pPhoneSensors->SetMagSource(PhoneSensors::MagnetometerSource_Native); Vector3f magPhone; pPhoneSensors->GetLatestMagValue(&magPhone); // Phone value is in micro-Tesla. Convert it to Gauss and flip axes. magPhone *= 10000.0f/1000000.0f; Vector3f res; res.x = -magPhone.y; res.y = magPhone.x; res.z = magPhone.z; *val = res; } #endif const int MAX_DEVICE_PROFILE_MAJOR_VERSION = 1; // Writes the current calibration for a particular device to a device profile file bool SensorDeviceImpl::SetMagCalibrationReport(const MagCalibrationReport &data) { // Get device info SensorInfo sinfo; GetDeviceInfo(&sinfo); // A named calibration may be specified for calibration in different // environments, otherwise the default calibration is used const char* calibrationName = "default"; // Generate a mag calibration event JSON* calibration = JSON::CreateObject(); // (hardcoded for now) the measurement and representation method calibration->AddStringItem("Version", "2.0"); calibration->AddStringItem("Name", "default"); // time stamp the calibration char time_str[64]; #ifdef OVR_OS_WIN32 struct tm caltime; time_t now = time(0); localtime_s(&caltime, &now); strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", &caltime); #else struct tm* caltime; time_t now = time(0); caltime = localtime(&now); strftime(time_str, 64, "%Y-%m-%d %H:%M:%S", caltime); #endif calibration->AddStringItem("Time", time_str); // write the full calibration matrix char matrix[256]; data.Calibration.ToString(matrix, 256); calibration->AddStringItem("CalibrationMatrix", matrix); // save just the offset, for backwards compatibility // this can be removed when we don't want to support 0.2.4 anymore Vector3f center(data.Calibration.M[0][3], data.Calibration.M[1][3], data.Calibration.M[2][3]); Matrix4f tmp = data.Calibration; tmp.M[0][3] = tmp.M[1][3] = tmp.M[2][3] = 0; tmp.M[3][3] = 1; center = tmp.Inverted().Transform(center); Matrix4f oldcalmat; oldcalmat.M[0][3] = center.x; oldcalmat.M[1][3] = center.y; oldcalmat.M[2][3] = center.z; oldcalmat.ToString(matrix, 256); calibration->AddStringItem("Calibration", matrix); String path = GetBaseOVRPath(true); path += "/Devices.json"; // Look for a preexisting device file to edit Ptr root = *JSON::Load(path); if (root) { // Quick sanity check of the file type and format before we parse it JSON* version = root->GetFirstItem(); if (version && version->Name == "Oculus Device Profile Version") { int major = atoi(version->Value.ToCStr()); if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION) { // don't use the file on unsupported major version number root->Release(); root = NULL; } } else { root->Release(); root = NULL; } } JSON* device = NULL; if (root) { device = root->GetFirstItem(); // skip the header device = root->GetNextItem(device); while (device) { // Search for a previous calibration with the same name for this device // and remove it before adding the new one if (device->Name == "Device") { JSON* item = device->GetItemByName("Serial"); if (item && item->Value == sinfo.SerialNumber) { // found an entry for this device item = device->GetNextItem(item); while (item) { if (item->Name == "MagCalibration") { JSON* name = item->GetItemByName("Name"); if (name && name->Value == calibrationName) { // found a calibration of the same name item->RemoveNode(); item->Release(); break; } } item = device->GetNextItem(item); } /* this is removed temporarily, since this is a sensor fusion setting, not sensor itself should be moved to the correct place when Brant has finished the user profile implementation // update the auto-mag flag item = device->GetItemByName("EnableYawCorrection"); if (item) item->dValue = (double)EnableYawCorrection; else device->AddBoolItem("EnableYawCorrection", EnableYawCorrection);*/ break; } } device = root->GetNextItem(device); } } else { // Create a new device root root = *JSON::CreateObject(); root->AddStringItem("Oculus Device Profile Version", "1.0"); } if (device == NULL) { device = JSON::CreateObject(); device->AddStringItem("Product", sinfo.ProductName); device->AddNumberItem("ProductID", sinfo.ProductId); device->AddStringItem("Serial", sinfo.SerialNumber); // removed temporarily, see above //device->AddBoolItem("EnableYawCorrection", EnableYawCorrection); root->AddItem("Device", device); } // Create and the add the new calibration event to the device device->AddItem("MagCalibration", calibration); return root->Save(path); } // Loads a saved calibration for the specified device from the device profile file bool SensorDeviceImpl::GetMagCalibrationReport(MagCalibrationReport* data) { data->Version = 0; data->Calibration.SetIdentity(); // Get device info SensorInfo sinfo; GetDeviceInfo(&sinfo); // A named calibration may be specified for calibration in different // environments, otherwise the default calibration is used const char* calibrationName = "default"; String path = GetBaseOVRPath(true); path += "/Devices.json"; // Load the device profiles Ptr root = *JSON::Load(path); if (root == NULL) return false; // Quick sanity check of the file type and format before we parse it JSON* version = root->GetFirstItem(); if (version && version->Name == "Oculus Device Profile Version") { int major = atoi(version->Value.ToCStr()); if (major > MAX_DEVICE_PROFILE_MAJOR_VERSION) return false; // don't parse the file on unsupported major version number } else { return false; } JSON* device = root->GetNextItem(version); while (device) { // Search for a previous calibration with the same name for this device // and remove it before adding the new one if (device->Name == "Device") { JSON* item = device->GetItemByName("Serial"); if (item && item->Value == sinfo.SerialNumber) { // found an entry for this device JSON* autoyaw = device->GetItemByName("EnableYawCorrection"); // as a temporary HACK, return no calibration if EnableYawCorrection is off // this will force disable yaw correction in SensorFusion // proper solution would load the value in the Profile, which SensorFusion can access if (autoyaw && autoyaw->dValue == 0) return true; item = device->GetNextItem(item); while (item) { if (item->Name == "MagCalibration") { JSON* calibration = item; JSON* name = calibration->GetItemByName("Name"); if (name && name->Value == calibrationName) { // found a calibration with this name int major = 0; JSON* version = calibration->GetItemByName("Version"); if (version) major = atoi(version->Value.ToCStr()); if (major > data->Version && major <= 2) { time_t now; time(&now); // parse the calibration time //time_t calibration_time = now; JSON* caltime = calibration->GetItemByName("Time"); if (caltime) { const char* caltime_str = caltime->Value.ToCStr(); tm ct; memset(&ct, 0, sizeof(tm)); #ifdef OVR_OS_WIN32 struct tm nowtime; localtime_s(&nowtime, &now); ct.tm_isdst = nowtime.tm_isdst; sscanf_s(caltime_str, "%d-%d-%d %d:%d:%d", &ct.tm_year, &ct.tm_mon, &ct.tm_mday, &ct.tm_hour, &ct.tm_min, &ct.tm_sec); #else struct tm* nowtime = localtime(&now); ct.tm_isdst = nowtime->tm_isdst; sscanf(caltime_str, "%d-%d-%d %d:%d:%d", &ct.tm_year, &ct.tm_mon, &ct.tm_mday, &ct.tm_hour, &ct.tm_min, &ct.tm_sec); #endif ct.tm_year -= 1900; ct.tm_mon--; //calibration_time = mktime(&ct); } // parse the calibration matrix JSON* cal = calibration->GetItemByName("CalibrationMatrix"); if (!cal) cal = calibration->GetItemByName("Calibration"); if (cal) { data->Calibration = Matrix4f::FromString(cal->Value.ToCStr()); data->Version = (UByte)major; } } } } item = device->GetNextItem(item); } return true; } } device = root->GetNextItem(device); } return true; } bool SensorDeviceImpl::SetSerialReport(const SerialReport& data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::setSerialReport, &result, data)) { return false; } return result; } bool SensorDeviceImpl::setSerialReport(const SerialReport& data) { SerialImpl di(data); return GetInternalDevice()->SetFeatureReport(di.Buffer, SerialImpl::PacketSize); } bool SensorDeviceImpl::GetSerialReport(SerialReport* data) { bool result; if (!GetManagerImpl()->GetThreadQueue()-> PushCallAndWaitResult(this, &Sensor2DeviceImpl::getSerialReport, &result, data)) { return false; } return result; } bool SensorDeviceImpl::getSerialReport(SerialReport* data) { SerialImpl di; if (GetInternalDevice()->GetFeatureReport(di.Buffer, SerialImpl::PacketSize)) { di.Unpack(); *data = di.Settings; return true; } return false; } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl.h new file mode 100644 index 0000000..ff628eb --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorImpl.h Content : Sensor device specific implementation. Created : March 7, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_SensorImpl_h #define OVR_SensorImpl_h #include "OVR_HIDDeviceImpl.h" #include "OVR_SensorTimeFilter.h" #include "OVR_Device.h" #ifdef OVR_OS_ANDROID #include "OVR_PhoneSensors.h" #endif namespace OVR { struct TrackerMessage; class ExternalVisitor; //------------------------------------------------------------------------------------- // SensorDeviceFactory enumerates Oculus Sensor devices. class SensorDeviceFactory : public DeviceFactory { public: static SensorDeviceFactory &GetInstance(); // Enumerates devices, creating and destroying relevant objects in manager. virtual void EnumerateDevices(EnumerateVisitor& visitor); virtual bool MatchVendorProduct(UInt16 vendorId, UInt16 productId) const; virtual bool DetectHIDDevice(DeviceManager* pdevMgr, const HIDDeviceDesc& desc); protected: DeviceManager* getManager() const { return (DeviceManager*) pManager; } }; // Describes a single a Oculus Sensor device and supports creating its instance. class SensorDeviceCreateDesc : public HIDDeviceCreateDesc { public: SensorDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) : HIDDeviceCreateDesc(factory, Device_Sensor, hidDesc) { } virtual DeviceCreateDesc* Clone() const { return new SensorDeviceCreateDesc(*this); } virtual DeviceBase* NewDeviceInstance(); virtual MatchResult MatchDevice(const DeviceCreateDesc& other, DeviceCreateDesc**) const { if ((other.Type == Device_Sensor) && (pFactory == other.pFactory)) { const SensorDeviceCreateDesc& s2 = (const SensorDeviceCreateDesc&) other; if (MatchHIDDevice(s2.HIDDesc)) return Match_Found; } return Match_None; } virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const { // should paths comparison be case insensitive? return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && (HIDDesc.SerialNumber == hidDesc.SerialNumber) && (HIDDesc.VersionNumber == hidDesc.VersionNumber)); } virtual bool GetDeviceInfo(DeviceInfo* info) const; }; // A simple stub for notification of a sensor in Boot Loader mode // This descriptor does not support the creation of a device, only the detection // of its existence to warn apps that the sensor device needs firmware. // The Boot Loader descriptor reuses and is created by the Sensor device factory // but in the future may use a dedicated factory class BootLoaderDeviceCreateDesc : public HIDDeviceCreateDesc { public: BootLoaderDeviceCreateDesc(DeviceFactory* factory, const HIDDeviceDesc& hidDesc) : HIDDeviceCreateDesc(factory, Device_BootLoader, hidDesc) { } virtual DeviceCreateDesc* Clone() const { return new BootLoaderDeviceCreateDesc(*this); } // Boot Loader device creation is not allowed virtual DeviceBase* NewDeviceInstance() { return NULL; }; virtual MatchResult MatchDevice(const DeviceCreateDesc& other, DeviceCreateDesc**) const { if ((other.Type == Device_BootLoader) && (pFactory == other.pFactory)) { const BootLoaderDeviceCreateDesc& s2 = (const BootLoaderDeviceCreateDesc&) other; if (MatchHIDDevice(s2.HIDDesc)) return Match_Found; } return Match_None; } virtual bool MatchHIDDevice(const HIDDeviceDesc& hidDesc) const { // should paths comparison be case insensitive? return ((HIDDesc.Path.CompareNoCase(hidDesc.Path) == 0) && (HIDDesc.SerialNumber == hidDesc.SerialNumber)); } virtual bool GetDeviceInfo(DeviceInfo* info) const { OVR_UNUSED(info); return false; } }; //------------------------------------------------------------------------------------- // ***** OVR::SensorDisplayInfoImpl // DisplayInfo obtained from sensor; these values are used to report distortion // settings and other coefficients. // Older SensorDisplayInfo will have all zeros, causing the library to apply hard-coded defaults. // Currently, only resolutions and sizes are used. struct SensorDisplayInfoImpl { enum { PacketSize = 56 }; UByte Buffer[PacketSize]; enum { Mask_BaseFmt = 0x0f, Mask_OptionFmts = 0xf0, Base_None = 0, Base_ScreenOnly = 1, Base_Distortion = 2, }; UInt16 CommandId; UByte DistortionType; UInt16 HResolution, VResolution; float HScreenSize, VScreenSize; float VCenter; float LensSeparation; // Currently these values are not well-measured. float OutsideLensSurfaceToScreen[2]; // TODO: add DistortionEqn // TODO: currently these values are all zeros and the // distortion is hard-coded in HMDDeviceCreateDesc::GetDeviceInfo() float DistortionK[6]; SensorDisplayInfoImpl(); void Unpack(); }; //------------------------------------------------------------------------------------- // ***** OVR::SensorDeviceImpl // Oculus Sensor interface. class SensorDeviceImpl : public HIDDeviceImpl { public: SensorDeviceImpl(SensorDeviceCreateDesc* createDesc); ~SensorDeviceImpl(); // DeviceCommaon interface virtual bool Initialize(DeviceBase* parent); virtual void Shutdown(); virtual void AddMessageHandler(MessageHandler* handler); // HIDDevice::Notifier interface. virtual void OnInputReport(UByte* pData, UInt32 length); virtual double OnTicks(double tickSeconds); // HMD-Mounted sensor has a different coordinate frame. virtual void SetCoordinateFrame(CoordinateFrame coordframe); virtual CoordinateFrame GetCoordinateFrame() const; // SensorDevice interface virtual bool SetRange(const SensorRange& range, bool waitFlag); virtual void GetRange(SensorRange* range) const; virtual void GetFactoryCalibration(Vector3f* AccelOffset, Vector3f* GyroOffset, Matrix4f* AccelMatrix, Matrix4f* GyroMatrix, float* Temperature); virtual void SetOnboardCalibrationEnabled(bool enabled); virtual bool IsMagCalibrated(); // Sets report rate (in Hz) of MessageBodyFrame messages (delivered through MessageHandler::OnMessage call). // Currently supported maximum rate is 1000Hz. If the rate is set to 500 or 333 Hz then OnMessage will be // called twice or thrice at the same 'tick'. // If the rate is < 333 then the OnMessage / MessageBodyFrame will be called three // times for each 'tick': the first call will contain averaged values, the second // and third calls will provide with most recent two recorded samples. virtual void SetReportRate(unsigned rateHz); // Returns currently set report rate, in Hz. If 0 - error occurred. // Note, this value may be different from the one provided for SetReportRate. The return // value will contain the actual rate. virtual unsigned GetReportRate() const; bool SetSerialReport(const SerialReport& data); bool GetSerialReport(SerialReport* data); // Hack to create HMD device from sensor display info. static void EnumerateHMDFromSensorDisplayInfo(const SensorDisplayInfoImpl& displayInfo, DeviceFactory::EnumerateVisitor& visitor); // These methods actually store data in a JSON file virtual bool SetMagCalibrationReport(const MagCalibrationReport& data); virtual bool GetMagCalibrationReport(MagCalibrationReport* data); protected: virtual void openDevice(); void closeDeviceOnError(); Void setCoordinateFrame(CoordinateFrame coordframe); bool setRange(const SensorRange& range); Void setReportRate(unsigned rateHz); Void setOnboardCalibrationEnabled(bool enabled); bool setSerialReport(const SerialReport& data); bool getSerialReport(SerialReport* data); // Called for decoded messages void onTrackerMessage(TrackerMessage* message); bool decodeTrackerMessage(TrackerMessage* message, UByte* buffer, int size); // Helpers to reduce casting. /* SensorDeviceCreateDesc* getCreateDesc() const { return (SensorDeviceCreateDesc*)pCreateDesc.GetPtr(); } HIDDeviceDesc* getHIDDesc() const { return &getCreateDesc()->HIDDesc; } */ // Set if the sensor is located on the HMD. // Older prototype firmware doesn't support changing HW coordinates, // so we track its state. CoordinateFrame Coordinates; CoordinateFrame HWCoordinates; double NextKeepAliveTickSeconds; bool SequenceValid; UInt16 LastTimestamp; UByte LastSampleCount; float LastTemperature; Vector3f LastAcceleration; Vector3f LastRotationRate; Vector3f LastMagneticField; // This tracks wrap around, and should be monotonically increasing. UInt32 FullTimestamp; // Current sensor range obtained from device. SensorRange MaxValidRange; SensorRange CurrentRange; // IMU calibration obtained from device. Vector3f AccelCalibrationOffset; Vector3f GyroCalibrationOffset; Matrix4f AccelCalibrationMatrix; Matrix4f GyroCalibrationMatrix; float CalibrationTemperature; UInt16 OldCommandId; SensorTimeFilter TimeFilter; double PrevAbsoluteTime; #ifdef OVR_OS_ANDROID void replaceWithPhoneMag(Vector3f* val); PhoneSensors* pPhoneSensors; #endif private: Matrix4f magCalibration; bool magCalibrated; }; } // namespace OVR #endif // OVR_SensorImpl_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl_Common.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl_Common.cpp new file mode 100644 index 0000000..0b75d70 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl_Common.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorImpl_Common.cpp Content : Source common to SensorImpl and Sensor2Impl. Created : January 21, 2014 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_SensorImpl_Common.h" #include "Kernel/OVR_Alg.h" namespace OVR { void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z) { // Sign extending trick // from http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend struct {SInt32 x:21;} s; *x = s.x = (buffer[0] << 13) | (buffer[1] << 5) | ((buffer[2] & 0xF8) >> 3); *y = s.x = ((buffer[2] & 0x07) << 18) | (buffer[3] << 10) | (buffer[4] << 2) | ((buffer[5] & 0xC0) >> 6); *z = s.x = ((buffer[5] & 0x3F) << 15) | (buffer[6] << 7) | (buffer[7] >> 1); } void PackSensor(UByte* buffer, SInt32 x, SInt32 y, SInt32 z) { // Pack 3 32 bit integers into 8 bytes buffer[0] = UByte(x >> 13); buffer[1] = UByte(x >> 5); buffer[2] = UByte((x << 3) | ((y >> 18) & 0x07)); buffer[3] = UByte(y >> 10); buffer[4] = UByte(y >> 2); buffer[5] = UByte((y << 6) | ((z >> 15) & 0x3F)); buffer[6] = UByte(z >> 7); buffer[7] = UByte(z << 1); } UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count, float val, float factor, const char* label) { UInt16 threshold = (UInt16)(val * factor); for (unsigned i = 0; i= threshold) return ramp[i]; } OVR_DEBUG_LOG(("SensorDevice::SetRange - %s clamped to %0.4f", label, float(ramp[count-1]) / factor)); OVR_UNUSED2(factor, label); return ramp[count-1]; } SensorRangeImpl::SensorRangeImpl(const SensorRange& r, UInt16 commandId) { SetSensorRange(r, commandId); } void SensorRangeImpl::SetSensorRange(const SensorRange& r, UInt16 commandId) { CommandId = commandId; AccelScale = SelectSensorRampValue(AccelRangeRamp, sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]), r.MaxAcceleration, (1.0f / 9.81f), "MaxAcceleration"); GyroScale = SelectSensorRampValue(GyroRangeRamp, sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]), r.MaxRotationRate, Math::RadToDegreeFactor, "MaxRotationRate"); MagScale = SelectSensorRampValue(MagRangeRamp, sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]), r.MaxMagneticField, 1000.0f, "MaxMagneticField"); Pack(); } void SensorRangeImpl::GetSensorRange(SensorRange* r) { r->MaxAcceleration = AccelScale * 9.81f; r->MaxRotationRate = DegreeToRad((float)GyroScale); r->MaxMagneticField= MagScale * 0.001f; } SensorRange SensorRangeImpl::GetMaxSensorRange() { return SensorRange(AccelRangeRamp[sizeof(AccelRangeRamp)/sizeof(AccelRangeRamp[0]) - 1] * 9.81f, GyroRangeRamp[sizeof(GyroRangeRamp)/sizeof(GyroRangeRamp[0]) - 1] * Math::DegreeToRadFactor, MagRangeRamp[sizeof(MagRangeRamp)/sizeof(MagRangeRamp[0]) - 1] * 0.001f); } void SensorRangeImpl::Pack() { Buffer[0] = 4; Buffer[1] = UByte(CommandId & 0xFF); Buffer[2] = UByte(CommandId >> 8); Buffer[3] = UByte(AccelScale); Buffer[4] = UByte(GyroScale & 0xFF); Buffer[5] = UByte(GyroScale >> 8); Buffer[6] = UByte(MagScale & 0xFF); Buffer[7] = UByte(MagScale >> 8); } void SensorRangeImpl::Unpack() { CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); AccelScale= Buffer[3]; GyroScale = Buffer[4] | (UInt16(Buffer[5]) << 8); MagScale = Buffer[6] | (UInt16(Buffer[7]) << 8); } SensorConfigImpl::SensorConfigImpl() : CommandId(0), Flags(0), PacketInterval(0), SampleRate(0) { memset(Buffer, 0, PacketSize); Buffer[0] = 2; } void SensorConfigImpl::SetSensorCoordinates(bool sensorCoordinates) { Flags = (Flags & ~Flag_SensorCoordinates) | (sensorCoordinates ? Flag_SensorCoordinates : 0); } bool SensorConfigImpl::IsUsingSensorCoordinates() const { return (Flags & Flag_SensorCoordinates) != 0; } void SensorConfigImpl::Pack() { Buffer[0] = 2; Buffer[1] = UByte(CommandId & 0xFF); Buffer[2] = UByte(CommandId >> 8); Buffer[3] = Flags; Buffer[4] = UByte(PacketInterval); Buffer[5] = UByte(SampleRate & 0xFF); Buffer[6] = UByte(SampleRate >> 8); } void SensorConfigImpl::Unpack() { CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); Flags = Buffer[3]; PacketInterval = Buffer[4]; SampleRate = Buffer[5] | (UInt16(Buffer[6]) << 8); } SensorFactoryCalibrationImpl::SensorFactoryCalibrationImpl() : AccelOffset(), GyroOffset(), AccelMatrix(), GyroMatrix(), Temperature(0) { memset(Buffer, 0, PacketSize); Buffer[0] = 3; } void SensorFactoryCalibrationImpl::Pack() { SInt32 x, y, z; Buffer[0] = 3; x = SInt32(AccelOffset.x * 1e4f); y = SInt32(AccelOffset.y * 1e4f); z = SInt32(AccelOffset.z * 1e4f); PackSensor(Buffer + 3, x, y, z); x = SInt32(GyroOffset.x * 1e4f); y = SInt32(GyroOffset.y * 1e4f); z = SInt32(GyroOffset.z * 1e4f); PackSensor(Buffer + 11, x, y, z); // ignore the scale matrices for now } void SensorFactoryCalibrationImpl::Unpack() { static const float sensorMax = (1 << 20) - 1; SInt32 x, y, z; UnpackSensor(Buffer + 3, &x, &y, &z); AccelOffset.y = (float) y * 1e-4f; AccelOffset.z = (float) z * 1e-4f; AccelOffset.x = (float) x * 1e-4f; UnpackSensor(Buffer + 11, &x, &y, &z); GyroOffset.x = (float) x * 1e-4f; GyroOffset.y = (float) y * 1e-4f; GyroOffset.z = (float) z * 1e-4f; for (int i = 0; i < 3; i++) { UnpackSensor(Buffer + 19 + 8 * i, &x, &y, &z); AccelMatrix.M[i][0] = (float) x / sensorMax; AccelMatrix.M[i][1] = (float) y / sensorMax; AccelMatrix.M[i][2] = (float) z / sensorMax; AccelMatrix.M[i][i] += 1.0f; } for (int i = 0; i < 3; i++) { UnpackSensor(Buffer + 43 + 8 * i, &x, &y, &z); GyroMatrix.M[i][0] = (float) x / sensorMax; GyroMatrix.M[i][1] = (float) y / sensorMax; GyroMatrix.M[i][2] = (float) z / sensorMax; GyroMatrix.M[i][i] += 1.0f; } Temperature = (float) Alg::DecodeSInt16(Buffer + 67) / 100.0f; } SensorKeepAliveImpl::SensorKeepAliveImpl(UInt16 interval, UInt16 commandId) : CommandId(commandId), KeepAliveIntervalMs(interval) { Pack(); } void SensorKeepAliveImpl::Pack() { Buffer[0] = 8; Buffer[1] = UByte(CommandId & 0xFF); Buffer[2] = UByte(CommandId >> 8); Buffer[3] = UByte(KeepAliveIntervalMs & 0xFF); Buffer[4] = UByte(KeepAliveIntervalMs >> 8); } void SensorKeepAliveImpl::Unpack() { CommandId = Buffer[1] | (UInt16(Buffer[2]) << 8); KeepAliveIntervalMs= Buffer[3] | (UInt16(Buffer[4]) << 8); } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl_Common.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl_Common.h new file mode 100644 index 0000000..7fef516 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorImpl_Common.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_SensorImpl_Common.h Content : Source common to SensorImpl and Sensor2Impl. Created : January 21, 2014 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_SensorImpl_Common_h #define OVR_SensorImpl_Common_h #include "Kernel/OVR_System.h" #include "OVR_Device.h" namespace OVR { void UnpackSensor(const UByte* buffer, SInt32* x, SInt32* y, SInt32* z); void PackSensor(UByte* buffer, SInt32 x, SInt32 y, SInt32 z); // Sensor HW only accepts specific maximum range values, used to maximize // the 16-bit sensor outputs. Use these ramps to specify and report appropriate values. const UInt16 AccelRangeRamp[] = { 2, 4, 8, 16 }; const UInt16 GyroRangeRamp[] = { 250, 500, 1000, 2000 }; const UInt16 MagRangeRamp[] = { 880, 1300, 1900, 2500 }; UInt16 SelectSensorRampValue(const UInt16* ramp, unsigned count, float val, float factor, const char* label); // SensorScaleImpl provides buffer packing logic for the Sensor Range // record that can be applied to DK1 sensor through Get/SetFeature. We expose this // through SensorRange class, which has different units. struct SensorRangeImpl { enum { PacketSize = 8 }; UByte Buffer[PacketSize]; UInt16 CommandId; UInt16 AccelScale; UInt16 GyroScale; UInt16 MagScale; SensorRangeImpl(const SensorRange& r, UInt16 commandId = 0); void SetSensorRange(const SensorRange& r, UInt16 commandId = 0); void GetSensorRange(SensorRange* r); static SensorRange GetMaxSensorRange(); void Pack(); void Unpack(); }; struct SensorConfigImpl { enum { PacketSize = 7 }; UByte Buffer[PacketSize]; // Flag values for Flags. enum { Flag_RawMode = 0x01, Flag_CalibrationTest = 0x02, // Internal test mode Flag_UseCalibration = 0x04, Flag_AutoCalibration = 0x08, Flag_MotionKeepAlive = 0x10, Flag_CommandKeepAlive = 0x20, Flag_SensorCoordinates = 0x40 }; UInt16 CommandId; UByte Flags; UInt16 PacketInterval; // LDC - This should be a UByte. Fix when you have time to test it. UInt16 SampleRate; SensorConfigImpl(); void SetSensorCoordinates(bool sensorCoordinates); bool IsUsingSensorCoordinates() const; void Pack(); void Unpack(); }; struct SensorFactoryCalibrationImpl { enum { PacketSize = 69 }; UByte Buffer[PacketSize]; Vector3f AccelOffset; Vector3f GyroOffset; Matrix4f AccelMatrix; Matrix4f GyroMatrix; float Temperature; SensorFactoryCalibrationImpl(); void Pack(); // Not yet implemented. void Unpack(); }; // SensorKeepAlive - feature report that needs to be sent at regular intervals for sensor // to receive commands. struct SensorKeepAliveImpl { enum { PacketSize = 5 }; UByte Buffer[PacketSize]; UInt16 CommandId; UInt16 KeepAliveIntervalMs; SensorKeepAliveImpl(UInt16 interval = 0, UInt16 commandId = 0); void Pack(); void Unpack(); }; struct TrackerSample { SInt32 AccelX, AccelY, AccelZ; SInt32 GyroX, GyroY, GyroZ; }; enum LastCommandIdFlags { LastCommandId_Shutter = 1, LastCommandId_LEDs = 2 }; } // namespace OVR #endif // OVR_SensorImpl_Common_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorTimeFilter.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorTimeFilter.cpp new file mode 100644 index 0000000..79088d0 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorTimeFilter.cpp @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_SensorTimeFilter.cpp Content : Class to filter HMD time and convert it to system time Created : December 20, 2013 Author : Michael Antonov Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_SensorTimeFilter.h" #include "Kernel/OVR_Log.h" #include #include namespace OVR { // Comment out for debug logging to file //#define OVR_TIMEFILTER_LOG_CODE( code ) code #define OVR_TIMEFILTER_LOG_CODE( code ) #if defined(OVR_OS_ANDROID) #define OVR_TIMEFILTER_LOG_FILENAME "/sdcard/TimeFilterLog.txt" #elif defined(OVR_OS_WIN32) #define OVR_TIMEFILTER_LOG_FILENAME "C:\\TimeFilterLog.txt" #else #define OVR_TIMEFILTER_LOG_FILENAME "TimeFilterLog.txt" #endif OVR_TIMEFILTER_LOG_CODE( FILE* pTFLogFile = 0; ) // Ideally, the following would always be true: // - NewSampleTime > PrevSample // - NewSampleTime < now systemTime // - (NewSampleTime - PrevSampleTime) == integration delta, matching // HW sample time difference + drift // // In practice, these issues affect us: // - System thread can be suspended for a while // - System de-buffering of recorded samples cause deviceTime to advance up // much faster then system time for ~100+ samples // - Device (DK1) and system clock granularities are high; this can // lead to potentially having estimations in the future // // ***** TimerFilter SensorTimeFilter::SensorTimeFilter(const Settings& settings) { FilterSettings = settings; ClockInitialized = false; ClockDelta = 0; ClockDeltaDriftPerSecond = 0; ClockDeltaCorrectPerSecond = 0; ClockDeltaCorrectSecondsLeft = 0; OldClockDeltaDriftExpire = 0; LastLargestDeviceTime = 0; PrevSystemTime = 0; PastSampleResetTime = 0; MinWindowsCollected = 0; MinWindowDuration = 0; // assigned later MinWindowLastTime = 0; MinWindowSamples = settings.MinSamples; // Force initialization OVR_TIMEFILTER_LOG_CODE( pTFLogFile = fopen(OVR_TIMEFILTER_LOG_FILENAME, "w+"); ) } double SensorTimeFilter::SampleToSystemTime(double sampleDeviceTime, double systemTime, double prevResult, const char* debugTag) { double clockDelta = systemTime - sampleDeviceTime + FilterSettings.ClockDeltaAdjust; double deviceTimeDelta = sampleDeviceTime - LastLargestDeviceTime; double result; // Collect a sample ClockDelta for a "MinimumWindow" or process // the window by adjusting drift rates if it's full of samples. // - (deviceTimeDelta < 1.0f) is a corner cases, as it would imply timestamp skip/wrap. if (ClockInitialized) { // Samples in the past commonly occur if they come from separately incrementing // data channels. Just adjust them with ClockDelta. if (deviceTimeDelta < 0.0) { result = sampleDeviceTime + ClockDelta; if (result > (prevResult - 0.00001)) goto clamp_and_log_result; // Consistent samples less then prevResult for indicate a back-jump or bad input. // In this case we return prevResult for a while, then reset filter if it keeps going. if (PastSampleResetTime < 0.0001) { PastSampleResetTime = systemTime + FilterSettings.PastSampleResetSeconds; goto clamp_and_log_result; } else if (systemTime > PastSampleResetTime) { OVR_DEBUG_LOG(("SensorTimeFilter - Filtering reset due to samples in the past!\n")); initClockSampling(sampleDeviceTime, clockDelta); // Fall through to below, to ' PastSampleResetTime = 0.0; ' } else { goto clamp_and_log_result; } } // Most common case: Record window sample. else if ( (deviceTimeDelta < 1.0f) && ( (sampleDeviceTime < MinWindowLastTime) || (MinWindowSamples < FilterSettings.MinSamples) ) ) { // Pick minimum ClockDelta sample. if (clockDelta < MinWindowClockDelta) MinWindowClockDelta = clockDelta; MinWindowSamples++; } else { processFinishedMinWindow(sampleDeviceTime, clockDelta); } PastSampleResetTime = 0.0; } else { initClockSampling(sampleDeviceTime, clockDelta); } // Clock adjustment for drift. ClockDelta += ClockDeltaDriftPerSecond * deviceTimeDelta; // ClockDelta "nudging" towards last known MinWindowClockDelta. if (ClockDeltaCorrectSecondsLeft > 0.000001) { double correctTimeDelta = deviceTimeDelta; if (deviceTimeDelta > ClockDeltaCorrectSecondsLeft) correctTimeDelta = ClockDeltaCorrectSecondsLeft; ClockDeltaCorrectSecondsLeft -= correctTimeDelta; ClockDelta += ClockDeltaCorrectPerSecond * correctTimeDelta; } // Record largest device time, so we know what samples to use in accumulation // of min-window in the future. LastLargestDeviceTime = sampleDeviceTime; // Compute our resulting sample time after ClockDelta adjustment. result = sampleDeviceTime + ClockDelta; clamp_and_log_result: OVR_TIMEFILTER_LOG_CODE( double savedResult = result; ) // Clamp to ensure that result >= PrevResult, or not to far in the future. // Future clamp primarily happens in the very beginning if we are de-queuing // system buffer full of samples. if (result < prevResult) { result = prevResult; } if (result > (systemTime + FilterSettings.FutureClamp)) { result = (systemTime + FilterSettings.FutureClamp); } OVR_TIMEFILTER_LOG_CODE( // Tag lines that were outside desired range, with '<' or '>'. char rangeClamp = ' '; char resultDeltaFar = ' '; if (savedResult > (systemTime + 0.0000001)) rangeClamp = '>'; if (savedResult < prevResult) rangeClamp = '<'; // Tag any result delta outside desired threshold with a '*'. if (fabs(deviceTimeDelta - (result - prevResult)) >= 0.00002) resultDeltaFar = '*'; fprintf(pTFLogFile, "Res%s = %13.7f, dt = % 8.7f, ClkD = %13.6f " "sysT = %13.6f, sysDt = %f, " "sysDiff = % f, devT = %11.6f, ddevT = %9.6f %c%c\n", debugTag, result, result - prevResult, ClockDelta, systemTime, systemTime - PrevSystemTime, -(systemTime - result), // Negatives in the past, positive > now. sampleDeviceTime, deviceTimeDelta, rangeClamp, resultDeltaFar); ) // OVR_TIMEFILTER_LOG_CODE() OVR_UNUSED(debugTag); // Record prior values. Useful or logging and clamping. PrevSystemTime = systemTime; return result; } void SensorTimeFilter::initClockSampling(double sampleDeviceTime, double clockDelta) { ClockInitialized = true; ClockDelta = clockDelta; ClockDeltaDriftPerSecond = 0; OldClockDeltaDriftExpire = 0; ClockDeltaCorrectSecondsLeft = 0; ClockDeltaCorrectPerSecond = 0; MinWindowsCollected = 0; MinWindowDuration = 0.25; MinWindowClockDelta = clockDelta; MinWindowLastTime = sampleDeviceTime + MinWindowDuration; MinWindowSamples = 0; } void SensorTimeFilter::processFinishedMinWindow(double sampleDeviceTime, double clockDelta) { MinRecord newRec = { MinWindowClockDelta, sampleDeviceTime }; double clockDeltaDiff = MinWindowClockDelta - ClockDelta; double absClockDeltaDiff = fabs(clockDeltaDiff); // Abrupt change causes Reset of minClockDelta collection. // > 8 ms would a Large jump in a minimum sample, as those are usually stable. // > 1 second intantaneous jump would land us here as well, as that would imply // device being suspended, clock wrap or some other unexpected issue. if ((absClockDeltaDiff > 0.008) || ((sampleDeviceTime - LastLargestDeviceTime) >= 1.0)) { OVR_TIMEFILTER_LOG_CODE( fprintf(pTFLogFile, "\nMinWindow Finished: %d Samples, MinWindowClockDelta=%f, MW-CD=%f," " ** ClockDelta Reset **\n\n", MinWindowSamples, MinWindowClockDelta, MinWindowClockDelta-ClockDelta); ) // Use old collected ClockDeltaDriftPerSecond drift value // up to 1 minute until we collect better samples. if (!MinRecords.IsEmpty()) { OldClockDeltaDriftExpire = MinRecords.GetNewest().LastSampleDeviceTime - MinRecords.GetOldest().LastSampleDeviceTime; if (OldClockDeltaDriftExpire > 60.0) OldClockDeltaDriftExpire = 60.0; OldClockDeltaDriftExpire += sampleDeviceTime; } // Jump to new ClockDelta value. if ((sampleDeviceTime - LastLargestDeviceTime) > 1.0) ClockDelta = clockDelta; else ClockDelta = MinWindowClockDelta; ClockDeltaCorrectSecondsLeft = 0; ClockDeltaCorrectPerSecond = 0; // Reset buffers, we'll be collecting a new MinWindow. MinRecords.Reset(); MinWindowsCollected = 0; MinWindowDuration = 0.25; MinWindowSamples = 0; } else { OVR_ASSERT(MinWindowSamples >= FilterSettings.MinSamples); double timeElapsed = 0; // If we have older values, use them to update clock drift in // ClockDeltaDriftPerSecond if (!MinRecords.IsEmpty() && (sampleDeviceTime > OldClockDeltaDriftExpire)) { MinRecord rec = MinRecords.GetOldest(); // Compute clock rate of drift. timeElapsed = sampleDeviceTime - rec.LastSampleDeviceTime; // Check for divide by zero shouldn't be necessary here, but just be be safe... if (timeElapsed > 0.000001) { ClockDeltaDriftPerSecond = (MinWindowClockDelta - rec.MinClockDelta) / timeElapsed; ClockDeltaDriftPerSecond = clampRate(ClockDeltaDriftPerSecond, FilterSettings.MaxChangeRate); } else { ClockDeltaDriftPerSecond = 0.0; } } MinRecords.AddRecord(newRec); // Catchup correction nudges ClockDelta towards MinWindowClockDelta. // These are needed because clock drift correction alone is not enough // for past accumulated error/high-granularity clock delta changes. // The further away we are, the stronger correction we apply. // Correction has timeout, as we don't want it to overshoot in case // of a large delay between samples. if (absClockDeltaDiff >= 0.00125) { // Correct large discrepancy immediately. if (absClockDeltaDiff > 0.00175) { if (clockDeltaDiff > 0) ClockDelta += (clockDeltaDiff - 0.00175); else ClockDelta += (clockDeltaDiff + 0.00175); clockDeltaDiff = MinWindowClockDelta - ClockDelta; } ClockDeltaCorrectPerSecond = clockDeltaDiff; ClockDeltaCorrectSecondsLeft = 1.0; } else if (absClockDeltaDiff > 0.0005) { ClockDeltaCorrectPerSecond = clockDeltaDiff / 8.0; ClockDeltaCorrectSecondsLeft = 8.0; } else { ClockDeltaCorrectPerSecond = clockDeltaDiff / 15.0; ClockDeltaCorrectSecondsLeft = 15.0; } ClockDeltaCorrectPerSecond = clampRate(ClockDeltaCorrectPerSecond, FilterSettings.MaxCorrectRate); OVR_TIMEFILTER_LOG_CODE( fprintf(pTFLogFile, "\nMinWindow Finished: %d Samples, MinWindowClockDelta=%f, MW-CD=%f," " tileElapsed=%f, ClockChange=%f, ClockCorrect=%f\n\n", MinWindowSamples, MinWindowClockDelta, MinWindowClockDelta-ClockDelta, timeElapsed, ClockDeltaDriftPerSecond, ClockDeltaCorrectPerSecond); ) } // New MinClockDelta collection window. // Switch to longer duration after first few windows. MinWindowsCollected ++; if (MinWindowsCollected > 5) MinWindowDuration = 0.5; MinWindowClockDelta = clockDelta; MinWindowLastTime = sampleDeviceTime + MinWindowDuration; MinWindowSamples = 0; } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorTimeFilter.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorTimeFilter.h new file mode 100644 index 0000000..952d4f0 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_SensorTimeFilter.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_SensorTimeFilter.h Content : Class to filter HMD time and convert it to system time Created : December 20, 2013 Author : Michael Antonov Notes : Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_SensorTimeFilter_h #define OVR_SensorTimeFilter_h #include "Kernel/OVR_Types.h" namespace OVR { //----------------------------------------------------------------------------------- // ***** SensorTimeFilter // SensorTimeFilter converts sample device time, in seconds, to absolute system // time. It filter maintains internal state to estimate the following: // // - Difference between system and device time values (ClockDelta). // ~= (systemTime - deviceTime) // - Drift rate between system and device clocks (ClockDeltaDriftPerSecond). // // Additionally, the following criteria are enforced: // - Resulting samples must be increasing, compared to prevSample. // - Returned sample time should not exceed 'now' system time by more then a fixed // value. // * Ideally this should be 0, however, enforcing this is hard when clocks // have high discrete values. // - Returned sample AbsoluteTime values deltas are very close to HW samples, // adjusted by drift rate. Note that this is not always possible due to clamping, // in which case it is better to use ScaleTimeUnit(deviceTimeDelta) // for integration. // // Algorithm: We collect minimum ClockDelta on windows of // consecutive samples (500 ms each set). Long term difference between sample // set minimums is drift. ClockDelta is also continually nudged towards most recent // minimum. class SensorTimeFilter { public: // It may be desirable to configure these per device/platform. // For example, rates can be tighter for DK2 because of microsecond clock. struct Settings { Settings(int minSamples = 50, double clockDeltaAdjust = -0.0002, // 200 mks in the past. double futureClamp = 0.0008) : MinSamples(minSamples), ClockDeltaAdjust(clockDeltaAdjust), // PastClamp(-0.032), FutureClamp(futureClamp), PastSampleResetSeconds(0.2), MaxChangeRate(0.004), MaxCorrectRate(0.004) { } // Minimum number of samples in a window. Different number may be desirable // based on how often samples come in. int MinSamples; // Factor always added to ClockDelta, used to skew all values into the past by fixed // value and reduce the chances we report a sample "in the future". double ClockDeltaAdjust; // How much away in a past can a sample be before being shifted closer to system time. //double PastClamp; // How much larger then systemTime can a value be? Set to 0 to clamp to null, // put small positive value is better. double FutureClamp; // How long (in system time) do we take to reset the system if a device sample. // comes in the past. Generally, this should never happened, but exists as a way to // address bad timing coming form firmware (temp CCove issue, presumably fixed) // or buggy input. double PastSampleResetSeconds; // Maximum drift change and near-term correction rates, in seconds. double MaxChangeRate; double MaxCorrectRate; }; SensorTimeFilter(const Settings& settings = Settings()); // Convert device sample time to system time, driving clock drift estimation. // Input: SampleTime, System Time // Return: Absolute system time for sample double SampleToSystemTime(double sampleDeviceTime, double systemTime, double prevResult, const char* debugTag = ""); // Scales device time to account for drift. double ScaleTimeUnit(double deviceClockDelta) { return deviceClockDelta * (1.0 + ClockDeltaDriftPerSecond); } // Return currently estimated difference between the clocks. double GetClockDelta() const { return ClockDelta; } private: void initClockSampling(double sampleDeviceTime, double clockDelta); void processFinishedMinWindow(double sampleDeviceTime, double systemTime); static double clampRate(double rate, double limit) { if (rate > limit) rate = limit; else if (rate < -limit) rate = -limit; return rate; } // Describes minimum observed ClockDelta for sample set seen in the past. struct MinRecord { double MinClockDelta; double LastSampleDeviceTime; }; // Circular buffer storing MinRecord(s) several minutes into the past. // Oldest value here is used to help estimate drift. class MinRecordBuffer { enum { BufferSize = 60*6 }; // 3 min public: MinRecordBuffer() : Head(0), Tail(0) { } void Reset() { Head = Tail = 0; } bool IsEmpty() const { return Head == Tail; } const MinRecord& GetOldest() const { OVR_ASSERT(!IsEmpty()); return Records[Tail]; } const MinRecord& GetNewest() const { OVR_ASSERT(!IsEmpty()); return Records[(BufferSize + Head - 1) % BufferSize]; } void AddRecord(const MinRecord& rec) { Records[Head] = rec; Head = advanceIndex(Head); if (Head == Tail) Tail = advanceIndex(Tail); } private: static int advanceIndex(int index) { index++; if (index >= BufferSize) index = 0; return index; } MinRecord Records[BufferSize]; int Head; // Location we will most recent entry, unused. int Tail; // Oldest entry. }; Settings FilterSettings; // Clock correction state. bool ClockInitialized; double ClockDelta; double ClockDeltaDriftPerSecond; double ClockDeltaCorrectPerSecond; double ClockDeltaCorrectSecondsLeft; double OldClockDeltaDriftExpire; double LastLargestDeviceTime; double PrevSystemTime; // Used to reset timing if we get multiple "samples in the past" double PastSampleResetTime; // "MinWindow" is a block of time during which minimum ClockDelta values // are collected into MinWindowClockDelta. int MinWindowsCollected; double MinWindowDuration; // Device sample seconds double MinWindowLastTime; double MinWindowClockDelta; int MinWindowSamples; // Historic buffer used to determine rate of clock change over time. MinRecordBuffer MinRecords; }; } // namespace OVR #endif // OVR_SensorTimeFilter_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Stereo.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Stereo.cpp new file mode 100644 index 0000000..042ff5b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Stereo.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : OVR_Stereo.cpp Content : Stereo rendering functions Created : November 30, 2013 Authors : Tom Fosyth Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_Stereo.h" #include "OVR_Profile.h" #include "Kernel/OVR_Log.h" #include "Kernel/OVR_Alg.h" //To allow custom distortion to be introduced to CatMulSpline. float (*CustomDistortion)(float) = NULL; float (*CustomDistortionInv)(float) = NULL; namespace OVR { using namespace Alg; //----------------------------------------------------------------------------------- // Inputs are 4 points (pFitX[0],pFitY[0]) through (pFitX[3],pFitY[3]) // Result is four coefficients in pResults[0] through pResults[3] such that // y = pResult[0] + x * ( pResult[1] + x * ( pResult[2] + x * ( pResult[3] ) ) ); // passes through all four input points. // Return is true if it succeeded, false if it failed (because two control points // have the same pFitX value). bool FitCubicPolynomial ( float *pResult, const float *pFitX, const float *pFitY ) { float d0 = ( ( pFitX[0]-pFitX[1] ) * ( pFitX[0]-pFitX[2] ) * ( pFitX[0]-pFitX[3] ) ); float d1 = ( ( pFitX[1]-pFitX[2] ) * ( pFitX[1]-pFitX[3] ) * ( pFitX[1]-pFitX[0] ) ); float d2 = ( ( pFitX[2]-pFitX[3] ) * ( pFitX[2]-pFitX[0] ) * ( pFitX[2]-pFitX[1] ) ); float d3 = ( ( pFitX[3]-pFitX[0] ) * ( pFitX[3]-pFitX[1] ) * ( pFitX[3]-pFitX[2] ) ); if ( ( d0 == 0.0f ) || ( d1 == 0.0f ) || ( d2 == 0.0f ) || ( d3 == 0.0f ) ) { return false; } float f0 = pFitY[0] / d0; float f1 = pFitY[1] / d1; float f2 = pFitY[2] / d2; float f3 = pFitY[3] / d3; pResult[0] = -( f0*pFitX[1]*pFitX[2]*pFitX[3] + f1*pFitX[0]*pFitX[2]*pFitX[3] + f2*pFitX[0]*pFitX[1]*pFitX[3] + f3*pFitX[0]*pFitX[1]*pFitX[2] ); pResult[1] = f0*(pFitX[1]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[1]) + f1*(pFitX[0]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[0]) + f2*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[3] + pFitX[3]*pFitX[0]) + f3*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[2] + pFitX[2]*pFitX[0]); pResult[2] = -( f0*(pFitX[1]+pFitX[2]+pFitX[3]) + f1*(pFitX[0]+pFitX[2]+pFitX[3]) + f2*(pFitX[0]+pFitX[1]+pFitX[3]) + f3*(pFitX[0]+pFitX[1]+pFitX[2]) ); pResult[3] = f0 + f1 + f2 + f3; return true; } float EvalCatmullRom10Spline ( float const *K, float scaledVal ) { int const NumSegments = LensConfig::NumCoefficients; float scaledValFloor = floorf ( scaledVal ); scaledValFloor = Alg::Max ( 0.0f, Alg::Min ( (float)(NumSegments-1), scaledValFloor ) ); float t = scaledVal - scaledValFloor; int k = (int)scaledValFloor; float p0, p1; float m0, m1; switch ( k ) { case 0: // Curve starts at 1.0 with gradient K[1]-K[0] p0 = 1.0f; m0 = ( K[1] - K[0] ); // general case would have been (K[1]-K[-1])/2 p1 = K[1]; m1 = 0.5f * ( K[2] - K[0] ); break; default: // General case p0 = K[k ]; m0 = 0.5f * ( K[k+1] - K[k-1] ); p1 = K[k+1]; m1 = 0.5f * ( K[k+2] - K[k ] ); break; case NumSegments-2: // Last tangent is just the slope of the last two points. p0 = K[NumSegments-2]; m0 = 0.5f * ( K[NumSegments-1] - K[NumSegments-2] ); p1 = K[NumSegments-1]; m1 = K[NumSegments-1] - K[NumSegments-2]; break; case NumSegments-1: // Beyond the last segment it's just a straight line p0 = K[NumSegments-1]; m0 = K[NumSegments-1] - K[NumSegments-2]; p1 = p0 + m0; m1 = m0; break; } float omt = 1.0f - t; float res = ( p0 * ( 1.0f + 2.0f * t ) + m0 * t ) * omt * omt + ( p1 * ( 1.0f + 2.0f * omt ) - m1 * omt ) * t * t; return res; } // Converts a Profile eyecup string into an eyecup enumeration void SetEyeCup(HmdRenderInfo* renderInfo, const char* cup) { if (OVR_strcmp(cup, "A") == 0) renderInfo->EyeCups = EyeCup_DK1A; else if (OVR_strcmp(cup, "B") == 0) renderInfo->EyeCups = EyeCup_DK1B; else if (OVR_strcmp(cup, "C") == 0) renderInfo->EyeCups = EyeCup_DK1C; else if (OVR_strcmp(cup, "Orange A") == 0) renderInfo->EyeCups = EyeCup_OrangeA; else if (OVR_strcmp(cup, "Red A") == 0) renderInfo->EyeCups = EyeCup_RedA; else if (OVR_strcmp(cup, "Pink A") == 0) renderInfo->EyeCups = EyeCup_PinkA; else if (OVR_strcmp(cup, "Blue A") == 0) renderInfo->EyeCups = EyeCup_BlueA; else renderInfo->EyeCups = EyeCup_DK1A; } //----------------------------------------------------------------------------------- // The result is a scaling applied to the distance. float LensConfig::DistortionFnScaleRadiusSquared (float rsq) const { float scale = 1.0f; switch ( Eqn ) { case Distortion_Poly4: // This version is deprecated! Prefer one of the other two. scale = ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) ); break; case Distortion_RecipPoly4: scale = 1.0f / ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) ); break; case Distortion_CatmullRom10:{ // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[10] // evenly spaced in R^2 from 0.0 to MaxR^2 // K[0] controls the slope at radius=0.0, rather than the actual value. const int NumSegments = LensConfig::NumCoefficients; OVR_ASSERT ( NumSegments <= NumCoefficients ); float scaledRsq = (float)(NumSegments-1) * rsq / ( MaxR * MaxR ); scale = EvalCatmullRom10Spline ( K, scaledRsq ); //Intercept, and overrule if needed if (CustomDistortion) { scale = CustomDistortion(rsq); } }break; default: OVR_ASSERT ( false ); break; } return scale; } // x,y,z components map to r,g,b Vector3f LensConfig::DistortionFnScaleRadiusSquaredChroma (float rsq) const { float scale = DistortionFnScaleRadiusSquared ( rsq ); Vector3f scaleRGB; scaleRGB.x = scale * ( 1.0f + ChromaticAberration[0] + rsq * ChromaticAberration[1] ); // Red scaleRGB.y = scale; // Green scaleRGB.z = scale * ( 1.0f + ChromaticAberration[2] + rsq * ChromaticAberration[3] ); // Blue return scaleRGB; } // DistortionFnInverse computes the inverse of the distortion function on an argument. float LensConfig::DistortionFnInverse(float r) const { OVR_ASSERT((r <= 20.0f)); float s, d; float delta = r * 0.25f; // Better to start guessing too low & take longer to converge than too high // and hit singularities. Empirically, r * 0.5f is too high in some cases. s = r * 0.25f; d = fabs(r - DistortionFn(s)); for (int i = 0; i < 20; i++) { float sUp = s + delta; float sDown = s - delta; float dUp = fabs(r - DistortionFn(sUp)); float dDown = fabs(r - DistortionFn(sDown)); if (dUp < d) { s = sUp; d = dUp; } else if (dDown < d) { s = sDown; d = dDown; } else { delta *= 0.5f; } } return s; } float LensConfig::DistortionFnInverseApprox(float r) const { float rsq = r * r; float scale = 1.0f; switch ( Eqn ) { case Distortion_Poly4: // Deprecated OVR_ASSERT ( false ); break; case Distortion_RecipPoly4: scale = 1.0f / ( InvK[0] + rsq * ( InvK[1] + rsq * ( InvK[2] + rsq * InvK[3] ) ) ); break; case Distortion_CatmullRom10:{ // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[9] // evenly spaced in R^2 from 0.0 to MaxR^2 // K[0] controls the slope at radius=0.0, rather than the actual value. const int NumSegments = LensConfig::NumCoefficients; OVR_ASSERT ( NumSegments <= NumCoefficients ); float scaledRsq = (float)(NumSegments-1) * rsq / ( MaxInvR * MaxInvR ); scale = EvalCatmullRom10Spline ( InvK, scaledRsq ); //Intercept, and overrule if needed if (CustomDistortionInv) { scale = CustomDistortionInv(rsq); } }break; default: OVR_ASSERT ( false ); break; } return r * scale; } void LensConfig::SetUpInverseApprox() { float maxR = MaxInvR; switch ( Eqn ) { case Distortion_Poly4: // Deprecated OVR_ASSERT ( false ); break; case Distortion_RecipPoly4:{ float sampleR[4]; float sampleRSq[4]; float sampleInv[4]; float sampleFit[4]; // Found heuristically... sampleR[0] = 0.0f; sampleR[1] = maxR * 0.4f; sampleR[2] = maxR * 0.8f; sampleR[3] = maxR * 1.5f; for ( int i = 0; i < 4; i++ ) { sampleRSq[i] = sampleR[i] * sampleR[i]; sampleInv[i] = DistortionFnInverse ( sampleR[i] ); sampleFit[i] = sampleR[i] / sampleInv[i]; } sampleFit[0] = 1.0f; FitCubicPolynomial ( InvK, sampleRSq, sampleFit ); #if 0 // Should be a nearly exact match on the chosen points. OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[0] ) - DistortionFnInverseApprox ( sampleR[0] ) ) / maxR < 0.0001f ); OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[1] ) - DistortionFnInverseApprox ( sampleR[1] ) ) / maxR < 0.0001f ); OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[2] ) - DistortionFnInverseApprox ( sampleR[2] ) ) / maxR < 0.0001f ); OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[3] ) - DistortionFnInverseApprox ( sampleR[3] ) ) / maxR < 0.0001f ); // Should be a decent match on the rest of the range. const int maxCheck = 20; for ( int i = 0; i < maxCheck; i++ ) { float checkR = (float)i * maxR / (float)maxCheck; float realInv = DistortionFnInverse ( checkR ); float testInv = DistortionFnInverseApprox ( checkR ); float error = fabsf ( realInv - testInv ) / maxR; OVR_ASSERT ( error < 0.1f ); } #endif }break; case Distortion_CatmullRom10:{ const int NumSegments = LensConfig::NumCoefficients; OVR_ASSERT ( NumSegments <= NumCoefficients ); for ( int i = 1; i < NumSegments; i++ ) { float scaledRsq = (float)i; float rsq = scaledRsq * MaxInvR * MaxInvR / (float)( NumSegments - 1); float r = sqrtf ( rsq ); float inv = DistortionFnInverse ( r ); InvK[i] = inv / r; InvK[0] = 1.0f; // TODO: fix this. } #if 0 const int maxCheck = 20; for ( int i = 0; i <= maxCheck; i++ ) { float checkR = (float)i * MaxInvR / (float)maxCheck; float realInv = DistortionFnInverse ( checkR ); float testInv = DistortionFnInverseApprox ( checkR ); float error = fabsf ( realInv - testInv ) / MaxR; OVR_ASSERT ( error < 0.01f ); } #endif }break; default: break; } } void LensConfig::SetToIdentity() { for ( int i = 0; i < NumCoefficients; i++ ) { K[i] = 0.0f; InvK[i] = 0.0f; } Eqn = Distortion_RecipPoly4; K[0] = 1.0f; InvK[0] = 1.0f; MaxR = 1.0f; MaxInvR = 1.0f; ChromaticAberration[0] = 0.0f; ChromaticAberration[1] = 0.0f; ChromaticAberration[2] = 0.0f; ChromaticAberration[3] = 0.0f; MetersPerTanAngleAtCenter = 0.05f; } enum LensConfigStoredVersion { LCSV_CatmullRom10Version1 = 1 }; // DO NOT CHANGE THESE ONCE THEY HAVE BEEN BAKED INTO FIRMWARE. // If something needs to change, add a new one! struct LensConfigStored_CatmullRom10Version1 { // All these items must be fixed-length integers - no "float", no "int", etc. UInt16 VersionNumber; // Must be LCSV_CatmullRom10Version1 UInt16 K[11]; UInt16 MaxR; UInt16 MetersPerTanAngleAtCenter; UInt16 ChromaticAberration[4]; // InvK and MaxInvR are calculated on load. }; UInt16 EncodeFixedPointUInt16 ( float val, UInt16 zeroVal, int fractionalBits ) { OVR_ASSERT ( ( fractionalBits >= 0 ) && ( fractionalBits < 31 ) ); float valWhole = val * (float)( 1 << fractionalBits ); valWhole += (float)zeroVal + 0.5f; valWhole = floorf ( valWhole ); OVR_ASSERT ( ( valWhole >= 0.0f ) && ( valWhole < (float)( 1 << 16 ) ) ); return (UInt16)valWhole; } float DecodeFixedPointUInt16 ( UInt16 val, UInt16 zeroVal, int fractionalBits ) { OVR_ASSERT ( ( fractionalBits >= 0 ) && ( fractionalBits < 31 ) ); float valFloat = (float)val; valFloat -= (float)zeroVal; valFloat *= 1.0f / (float)( 1 << fractionalBits ); return valFloat; } // Returns true on success. bool LoadLensConfig ( LensConfig *presult, UByte const *pbuffer, int bufferSizeInBytes ) { if ( bufferSizeInBytes < 2 ) { // Can't even tell the version number! return false; } UInt16 version = DecodeUInt16 ( pbuffer + 0 ); switch ( version ) { case LCSV_CatmullRom10Version1: { if ( bufferSizeInBytes < (int)sizeof(LensConfigStored_CatmullRom10Version1) ) { return false; } LensConfigStored_CatmullRom10Version1 lcs; lcs.VersionNumber = DecodeUInt16 ( pbuffer + 0 ); for ( int i = 0; i < 11; i++ ) { lcs.K[i] = DecodeUInt16 ( pbuffer + 2 + 2*i ); } lcs.MaxR = DecodeUInt16 ( pbuffer + 24 ); lcs.MetersPerTanAngleAtCenter = DecodeUInt16 ( pbuffer + 26 ); for ( int i = 0; i < 4; i++ ) { lcs.ChromaticAberration[i] = DecodeUInt16 ( pbuffer + 28 + 2*i ); } OVR_COMPILER_ASSERT ( sizeof(lcs) == 36 ); // Convert to the real thing. LensConfig result; result.Eqn = Distortion_CatmullRom10; for ( int i = 0; i < 11; i++ ) { // K[] are mostly 1.something. They may get significantly bigger, but they never hit 0.0. result.K[i] = DecodeFixedPointUInt16 ( lcs.K[i], 0, 14 ); } // MaxR is tan(angle), so always >0, typically just over 1.0 (45 degrees half-fov), // but may get arbitrarily high. tan(76)=4 is a very reasonable limit! result.MaxR = DecodeFixedPointUInt16 ( lcs.MaxR, 0, 14 ); // MetersPerTanAngleAtCenter is also known as focal length! // Typically around 0.04 for our current screens, minimum of 0, sensible maximum of 0.125 (i.e. 3 "extra" bits of fraction) result.MetersPerTanAngleAtCenter = DecodeFixedPointUInt16 ( lcs.MetersPerTanAngleAtCenter, 0, 16+3 ); for ( int i = 0; i < 4; i++ ) { // ChromaticAberration[] are mostly 0.0something, centered on 0.0. Largest seen is 0.04, so set max to 0.125 (i.e. 3 "extra" bits of fraction) result.ChromaticAberration[i] = DecodeFixedPointUInt16 ( lcs.ChromaticAberration[i], 0x8000, 16+3 ); } result.MaxInvR = result.DistortionFn ( result.MaxR ); result.SetUpInverseApprox(); OVR_ASSERT ( version == lcs.VersionNumber ); *presult = result; } break; default: // Unknown format. return false; break; } return true; } // Returns number of bytes needed. int SaveLensConfigSizeInBytes ( LensConfig const &config ) { OVR_UNUSED ( config ); return sizeof ( LensConfigStored_CatmullRom10Version1 ); } // Returns true on success. bool SaveLensConfig ( UByte *pbuffer, int bufferSizeInBytes, LensConfig const &config ) { if ( bufferSizeInBytes < (int)sizeof ( LensConfigStored_CatmullRom10Version1 ) ) { return false; } // Construct the values. LensConfigStored_CatmullRom10Version1 lcs; lcs.VersionNumber = LCSV_CatmullRom10Version1; for ( int i = 0; i < 11; i++ ) { // K[] are mostly 1.something. They may get significantly bigger, but they never hit 0.0. lcs.K[i] = EncodeFixedPointUInt16 ( config.K[i], 0, 14 ); } // MaxR is tan(angle), so always >0, typically just over 1.0 (45 degrees half-fov), // but may get arbitrarily high. tan(76)=4 is a very reasonable limit! lcs.MaxR = EncodeFixedPointUInt16 ( config.MaxR, 0, 14 ); // MetersPerTanAngleAtCenter is also known as focal length! // Typically around 0.04 for our current screens, minimum of 0, sensible maximum of 0.125 (i.e. 3 "extra" bits of fraction) lcs.MetersPerTanAngleAtCenter = EncodeFixedPointUInt16 ( config.MetersPerTanAngleAtCenter, 0, 16+3 ); for ( int i = 0; i < 4; i++ ) { // ChromaticAberration[] are mostly 0.0something, centered on 0.0. Largest seen is 0.04, so set max to 0.125 (i.e. 3 "extra" bits of fraction) lcs.ChromaticAberration[i] = EncodeFixedPointUInt16 ( config.ChromaticAberration[i], 0x8000, 16+3 ); } // Now store them out, sensitive to endinness. EncodeUInt16 ( pbuffer + 0, lcs.VersionNumber ); for ( int i = 0; i < 11; i++ ) { EncodeUInt16 ( pbuffer + 2 + 2*i, lcs.K[i] ); } EncodeUInt16 ( pbuffer + 24, lcs.MaxR ); EncodeUInt16 ( pbuffer + 26, lcs.MetersPerTanAngleAtCenter ); for ( int i = 0; i < 4; i++ ) { EncodeUInt16 ( pbuffer + 28 + 2*i, lcs.ChromaticAberration[i] ); } OVR_COMPILER_ASSERT ( 36 == sizeof(lcs) ); return true; } #ifdef OVR_BUILD_DEBUG void TestSaveLoadLensConfig ( LensConfig const &config ) { OVR_ASSERT ( config.Eqn == Distortion_CatmullRom10 ); // As a test, make sure this can be encoded and decoded correctly. const int bufferSize = 256; UByte buffer[bufferSize]; OVR_ASSERT ( SaveLensConfigSizeInBytes ( config ) < bufferSize ); bool success; success = SaveLensConfig ( buffer, bufferSize, config ); OVR_ASSERT ( success ); LensConfig testConfig; success = LoadLensConfig ( &testConfig, buffer, bufferSize ); OVR_ASSERT ( success ); OVR_ASSERT ( testConfig.Eqn == config.Eqn ); for ( int i = 0; i < 11; i++ ) { OVR_ASSERT ( fabs ( testConfig.K[i] - config.K[i] ) < 0.0001f ); } OVR_ASSERT ( fabsf ( testConfig.MaxR - config.MaxR ) < 0.0001f ); OVR_ASSERT ( fabsf ( testConfig.MetersPerTanAngleAtCenter - config.MetersPerTanAngleAtCenter ) < 0.00001f ); for ( int i = 0; i < 4; i++ ) { OVR_ASSERT ( fabsf ( testConfig.ChromaticAberration[i] - config.ChromaticAberration[i] ) < 0.00001f ); } } #endif //----------------------------------------------------------------------------------- // TBD: There is a question of whether this is the best file for CreateDebugHMDInfo. As long as there are many // constants for HmdRenderInfo here as well it is ok. The alternative would be OVR_Common_HMDDevice.cpp, but // that's specialized per platform... should probably move it there onces the code is in the common base class. HMDInfo CreateDebugHMDInfo(HmdTypeEnum hmdType) { HMDInfo info; if ((hmdType != HmdType_DK1) && (hmdType != HmdType_CrystalCoveProto)) { LogText("Debug HMDInfo - HmdType not supported. Defaulting to DK1.\n"); hmdType = HmdType_DK1; } // The alternative would be to initialize info.HmdType to HmdType_None instead. If we did that, // code wouldn't be "maximally compatible" and devs wouldn't know what device we are // simulating... so if differentiation becomes necessary we better add Debug flag in the future. info.HmdType = hmdType; info.Manufacturer = "Oculus VR"; switch(hmdType) { case HmdType_DK1: info.ProductName = "Oculus Rift DK1"; info.ResolutionInPixels = Sizei ( 1280, 800 ); info.ScreenSizeInMeters = Sizef ( 0.1498f, 0.0936f ); info.ScreenGapSizeInMeters = 0.0f; info.CenterFromTopInMeters = 0.0468f; info.LensSeparationInMeters = 0.0635f; info.Shutter.Type = HmdShutter_RollingTopToBottom; info.Shutter.VsyncToNextVsync = ( 1.0f / 60.0f ); info.Shutter.VsyncToFirstScanline = 0.000052f; info.Shutter.FirstScanlineToLastScanline = 0.016580f; info.Shutter.PixelSettleTime = 0.015f; info.Shutter.PixelPersistence = ( 1.0f / 60.0f ); break; case HmdType_CrystalCoveProto: info.ProductName = "Oculus Rift Crystal Cove"; info.ResolutionInPixels = Sizei ( 1920, 1080 ); info.ScreenSizeInMeters = Sizef ( 0.12576f, 0.07074f ); info.ScreenGapSizeInMeters = 0.0f; info.CenterFromTopInMeters = info.ScreenSizeInMeters.h * 0.5f; info.LensSeparationInMeters = 0.0635f; info.Shutter.Type = HmdShutter_RollingRightToLeft; info.Shutter.VsyncToNextVsync = ( 1.0f / 76.0f ); info.Shutter.VsyncToFirstScanline = 0.0000273f; info.Shutter.FirstScanlineToLastScanline = 0.0131033f; info.Shutter.PixelSettleTime = 0.0f; info.Shutter.PixelPersistence = 0.18f * info.Shutter.VsyncToNextVsync; break; case HmdType_DK2: info.ProductName = "Oculus Rift DK2"; info.ResolutionInPixels = Sizei ( 1920, 1080 ); info.ScreenSizeInMeters = Sizef ( 0.12576f, 0.07074f ); info.ScreenGapSizeInMeters = 0.0f; info.CenterFromTopInMeters = info.ScreenSizeInMeters.h * 0.5f; info.LensSeparationInMeters = 0.0635f; info.Shutter.Type = HmdShutter_RollingRightToLeft; info.Shutter.VsyncToNextVsync = ( 1.0f / 76.0f ); info.Shutter.VsyncToFirstScanline = 0.0000273f; info.Shutter.FirstScanlineToLastScanline = 0.0131033f; info.Shutter.PixelSettleTime = 0.0f; info.Shutter.PixelPersistence = 0.18f * info.Shutter.VsyncToNextVsync; break; default: break; } return info; } // profile may be NULL, in which case it uses the hard-coded defaults. HmdRenderInfo GenerateHmdRenderInfoFromHmdInfo ( HMDInfo const &hmdInfo, Profile const *profile /*=NULL*/, DistortionEqnType distortionType /*= Distortion_CatmullRom10*/, EyeCupType eyeCupOverride /*= EyeCup_LAST*/ ) { HmdRenderInfo renderInfo; renderInfo.HmdType = hmdInfo.HmdType; renderInfo.ResolutionInPixels = hmdInfo.ResolutionInPixels; renderInfo.ScreenSizeInMeters = hmdInfo.ScreenSizeInMeters; renderInfo.CenterFromTopInMeters = hmdInfo.CenterFromTopInMeters; renderInfo.ScreenGapSizeInMeters = hmdInfo.ScreenGapSizeInMeters; renderInfo.LensSeparationInMeters = hmdInfo.LensSeparationInMeters; OVR_ASSERT ( sizeof(renderInfo.Shutter) == sizeof(hmdInfo.Shutter) ); // Try to keep the files in sync! renderInfo.Shutter.Type = hmdInfo.Shutter.Type; renderInfo.Shutter.VsyncToNextVsync = hmdInfo.Shutter.VsyncToNextVsync; renderInfo.Shutter.VsyncToFirstScanline = hmdInfo.Shutter.VsyncToFirstScanline; renderInfo.Shutter.FirstScanlineToLastScanline = hmdInfo.Shutter.FirstScanlineToLastScanline; renderInfo.Shutter.PixelSettleTime = hmdInfo.Shutter.PixelSettleTime; renderInfo.Shutter.PixelPersistence = hmdInfo.Shutter.PixelPersistence; renderInfo.LensDiameterInMeters = 0.035f; renderInfo.LensSurfaceToMidplateInMeters = 0.025f; renderInfo.EyeCups = EyeCup_DK1A; #if 0 // Device settings are out of date - don't use them. if (Contents & Contents_Distortion) { memcpy(renderInfo.DistortionK, DistortionK, sizeof(float)*4); renderInfo.DistortionEqn = Distortion_RecipPoly4; } #endif // Defaults in case of no user profile. renderInfo.EyeLeft.NoseToPupilInMeters = 0.032f; renderInfo.EyeLeft.ReliefInMeters = 0.012f; // 10mm eye-relief laser numbers for DK1 lenses. // These are a decent seed for finding eye-relief and IPD. // These are NOT used for rendering! // Rendering distortions are now in GenerateLensConfigFromEyeRelief() // So, if you're hacking in new distortions, don't do it here! renderInfo.EyeLeft.Distortion.SetToIdentity(); renderInfo.EyeLeft.Distortion.MetersPerTanAngleAtCenter = 0.0449f; renderInfo.EyeLeft.Distortion.Eqn = Distortion_RecipPoly4; renderInfo.EyeLeft.Distortion.K[0] = 1.0f; renderInfo.EyeLeft.Distortion.K[1] = -0.494165344f; renderInfo.EyeLeft.Distortion.K[2] = 0.587046423f; renderInfo.EyeLeft.Distortion.K[3] = -0.841887126f; renderInfo.EyeLeft.Distortion.MaxR = 1.0f; renderInfo.EyeLeft.Distortion.ChromaticAberration[0] = -0.006f; renderInfo.EyeLeft.Distortion.ChromaticAberration[1] = 0.0f; renderInfo.EyeLeft.Distortion.ChromaticAberration[2] = 0.014f; renderInfo.EyeLeft.Distortion.ChromaticAberration[3] = 0.0f; renderInfo.EyeRight = renderInfo.EyeLeft; // Obtain data from profile. if ( profile != NULL ) { char eyecup[16]; if (profile->GetValue(OVR_KEY_EYE_CUP, eyecup, 16)) SetEyeCup(&renderInfo, eyecup); } switch ( hmdInfo.HmdType ) { case HmdType_None: case HmdType_DKProto: case HmdType_DK1: // Slight hack to improve usability. // If you have a DKHD-style lens profile enabled, // but you plug in DK1 and forget to change the profile, // obviously you don't want those lens numbers. if ( ( renderInfo.EyeCups != EyeCup_DK1A ) && ( renderInfo.EyeCups != EyeCup_DK1B ) && ( renderInfo.EyeCups != EyeCup_DK1C ) ) { renderInfo.EyeCups = EyeCup_DK1A; } break; case HmdType_DKHD2Proto: renderInfo.EyeCups = EyeCup_DKHD2A; break; case HmdType_CrystalCoveProto: renderInfo.EyeCups = EyeCup_PinkA; break; case HmdType_DK2: renderInfo.EyeCups = EyeCup_DK2A; break; default: break; } if ( eyeCupOverride != EyeCup_LAST ) { renderInfo.EyeCups = eyeCupOverride; } switch ( renderInfo.EyeCups ) { case EyeCup_DK1A: case EyeCup_DK1B: case EyeCup_DK1C: renderInfo.LensDiameterInMeters = 0.035f; renderInfo.LensSurfaceToMidplateInMeters = 0.02357f; // Not strictly lens-specific, but still wise to set a reasonable default for relief. renderInfo.EyeLeft.ReliefInMeters = 0.010f; renderInfo.EyeRight.ReliefInMeters = 0.010f; break; case EyeCup_DKHD2A: renderInfo.LensDiameterInMeters = 0.035f; renderInfo.LensSurfaceToMidplateInMeters = 0.02357f; // Not strictly lens-specific, but still wise to set a reasonable default for relief. renderInfo.EyeLeft.ReliefInMeters = 0.010f; renderInfo.EyeRight.ReliefInMeters = 0.010f; break; case EyeCup_PinkA: case EyeCup_DK2A: renderInfo.LensDiameterInMeters = 0.04f; // approximate renderInfo.LensSurfaceToMidplateInMeters = 0.01965f; // Not strictly lens-specific, but still wise to set a reasonable default for relief. renderInfo.EyeLeft.ReliefInMeters = 0.012f; renderInfo.EyeRight.ReliefInMeters = 0.012f; break; default: OVR_ASSERT ( false ); break; } if ( profile != NULL ) { // Set the customized user eye position // TBD: Maybe we should separate custom camera positioning from custom distortion rendering ?? if (profile->GetBoolValue(OVR_KEY_CUSTOM_EYE_RENDER, true)) { float eye2nose[2]; if (profile->GetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, eye2nose, 2) == 2) { // Load per-eye half-IPD renderInfo.EyeLeft.NoseToPupilInMeters = eye2nose[0]; renderInfo.EyeRight.NoseToPupilInMeters = eye2nose[1]; } else { // Use a centered IPD instead float ipd = profile->GetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD); renderInfo.EyeLeft.NoseToPupilInMeters = 0.5f * ipd; renderInfo.EyeRight.NoseToPupilInMeters = 0.5f * ipd; } float eye2plate[2]; if (profile->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, eye2plate, 2) == 2) { // Subtract the eye-cup height from the plate distance to get the eye-to-lens distance // This measurement should be the the distance at maximum dial setting // We still need to adjust with the dial offset renderInfo.EyeLeft.ReliefInMeters = eye2plate[0] - renderInfo.LensSurfaceToMidplateInMeters; renderInfo.EyeRight.ReliefInMeters = eye2plate[1] - renderInfo.LensSurfaceToMidplateInMeters; // Adjust the eye relief with the dial setting (from the assumed max eye relief) int dial = profile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, -1); if (dial >= 0) { renderInfo.EyeLeft.ReliefInMeters -= ((10 - dial) * 0.001f); renderInfo.EyeRight.ReliefInMeters -= ((10 - dial) * 0.001f); } } else { // Set the eye relief with the user configured dial setting int dial = profile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, -1); if (dial >= 0) { // Assume a default of 7 to 17 mm eye relief based on the dial. This corresponds // to the sampled and tuned distortion range on the DK1. renderInfo.EyeLeft.ReliefInMeters = 0.007f + (dial * 0.001f); renderInfo.EyeRight.ReliefInMeters = 0.007f + (dial * 0.001f); } } } } // Now we know where the eyes are relative to the lenses, we can compute a distortion for each. // TODO: incorporate lateral offset in distortion generation. // TODO: we used a distortion to calculate eye-relief, and now we're making a distortion from that eye-relief. Close the loop! for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { HmdRenderInfo::EyeConfig *pHmdEyeConfig = ( eyeNum == 0 ) ? &(renderInfo.EyeLeft) : &(renderInfo.EyeRight); float eye_relief = pHmdEyeConfig->ReliefInMeters; LensConfig distortionConfig = GenerateLensConfigFromEyeRelief ( eye_relief, renderInfo, distortionType ); pHmdEyeConfig->Distortion = distortionConfig; } return renderInfo; } LensConfig GenerateLensConfigFromEyeRelief ( float eyeReliefInMeters, HmdRenderInfo const &hmd, DistortionEqnType distortionType /*= Distortion_CatmullRom10*/ ) { struct DistortionDescriptor { float EyeRelief; // The three places we're going to sample & lerp the curve at. // One sample is always at 0.0, and the distortion scale should be 1.0 or else! // Only use for poly4 numbers - CR has an implicit scale. float SampleRadius[3]; // Where the distortion has actually been measured/calibrated out to. // Don't try to hallucinate data out beyond here. float MaxRadius; // The config itself. LensConfig Config; }; DistortionDescriptor distortions[10]; for ( unsigned int i = 0; i < sizeof(distortions)/sizeof(distortions[0]); i++ ) { distortions[i].Config.SetToIdentity(); distortions[i].EyeRelief = 0.0f; distortions[i].MaxRadius = 1.0f; } int numDistortions = 0; int defaultDistortion = 0; // index of the default distortion curve to use if zero eye relief supplied if ( ( hmd.EyeCups == EyeCup_DK1A ) || ( hmd.EyeCups == EyeCup_DK1B ) || ( hmd.EyeCups == EyeCup_DK1C ) ) { numDistortions = 0; // Tuned at minimum dial setting - extended to r^2 == 1.8 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10; distortions[numDistortions].EyeRelief = 0.012760465f - 0.005f; distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f; distortions[numDistortions].Config.K[0] = 1.0000f; distortions[numDistortions].Config.K[1] = 1.06505f; distortions[numDistortions].Config.K[2] = 1.14725f; distortions[numDistortions].Config.K[3] = 1.2705f; distortions[numDistortions].Config.K[4] = 1.48f; distortions[numDistortions].Config.K[5] = 1.87f; distortions[numDistortions].Config.K[6] = 2.534f; distortions[numDistortions].Config.K[7] = 3.6f; distortions[numDistortions].Config.K[8] = 5.1f; distortions[numDistortions].Config.K[9] = 7.4f; distortions[numDistortions].Config.K[10] = 11.0f; distortions[numDistortions].SampleRadius[0] = 0.222717149f; distortions[numDistortions].SampleRadius[1] = 0.512249443f; distortions[numDistortions].SampleRadius[2] = 0.712694878f; distortions[numDistortions].MaxRadius = sqrt(1.8f); defaultDistortion = numDistortions; // this is the default numDistortions++; // Tuned at middle dial setting distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10; distortions[numDistortions].EyeRelief = 0.012760465f; // my average eye-relief distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f; distortions[numDistortions].Config.K[0] = 1.0f; distortions[numDistortions].Config.K[1] = 1.032407264f; distortions[numDistortions].Config.K[2] = 1.07160462f; distortions[numDistortions].Config.K[3] = 1.11998388f; distortions[numDistortions].Config.K[4] = 1.1808606f; distortions[numDistortions].Config.K[5] = 1.2590494f; distortions[numDistortions].Config.K[6] = 1.361915f; distortions[numDistortions].Config.K[7] = 1.5014339f; distortions[numDistortions].Config.K[8] = 1.6986004f; distortions[numDistortions].Config.K[9] = 1.9940577f; distortions[numDistortions].Config.K[10] = 2.4783147f; distortions[numDistortions].SampleRadius[0] = 0.222717149f; distortions[numDistortions].SampleRadius[1] = 0.512249443f; distortions[numDistortions].SampleRadius[2] = 0.712694878f; distortions[numDistortions].MaxRadius = 1.0f; numDistortions++; // Tuned at maximum dial setting distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10; distortions[numDistortions].EyeRelief = 0.012760465f + 0.005f; distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f; distortions[numDistortions].Config.K[0] = 1.0102f; distortions[numDistortions].Config.K[1] = 1.0371f; distortions[numDistortions].Config.K[2] = 1.0831f; distortions[numDistortions].Config.K[3] = 1.1353f; distortions[numDistortions].Config.K[4] = 1.2f; distortions[numDistortions].Config.K[5] = 1.2851f; distortions[numDistortions].Config.K[6] = 1.3979f; distortions[numDistortions].Config.K[7] = 1.56f; distortions[numDistortions].Config.K[8] = 1.8f; distortions[numDistortions].Config.K[9] = 2.25f; distortions[numDistortions].Config.K[10] = 3.0f; distortions[numDistortions].SampleRadius[0] = 0.222717149f; distortions[numDistortions].SampleRadius[1] = 0.512249443f; distortions[numDistortions].SampleRadius[2] = 0.712694878f; distortions[numDistortions].MaxRadius = 1.0f; numDistortions++; // Chromatic aberration doesn't seem to change with eye relief. for ( int i = 0; i < numDistortions; i++ ) { distortions[i].Config.ChromaticAberration[0] = -0.006f; distortions[i].Config.ChromaticAberration[1] = 0.0f; distortions[i].Config.ChromaticAberration[2] = 0.014f; distortions[i].Config.ChromaticAberration[3] = 0.0f; } } else if ( hmd.EyeCups == EyeCup_DKHD2A ) { // Tuned DKHD2 lens numDistortions = 0; distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10; distortions[numDistortions].EyeRelief = 0.010f; distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f; distortions[numDistortions].Config.K[0] = 1.0f; distortions[numDistortions].Config.K[1] = 1.0425f; distortions[numDistortions].Config.K[2] = 1.0826f; distortions[numDistortions].Config.K[3] = 1.130f; distortions[numDistortions].Config.K[4] = 1.185f; distortions[numDistortions].Config.K[5] = 1.250f; distortions[numDistortions].Config.K[6] = 1.338f; distortions[numDistortions].Config.K[7] = 1.455f; distortions[numDistortions].Config.K[8] = 1.620f; distortions[numDistortions].Config.K[9] = 1.840f; distortions[numDistortions].Config.K[10] = 2.200f; distortions[numDistortions].SampleRadius[0] = 0.222717149f; distortions[numDistortions].SampleRadius[1] = 0.512249443f; distortions[numDistortions].SampleRadius[2] = 0.712694878f; distortions[numDistortions].MaxRadius = 1.0f; distortions[numDistortions].SampleRadius[0] = 0.405405405f; distortions[numDistortions].SampleRadius[1] = 0.675675676f; distortions[numDistortions].SampleRadius[2] = 0.945945946f; defaultDistortion = numDistortions; // this is the default numDistortions++; distortions[numDistortions] = distortions[0]; distortions[numDistortions].EyeRelief = 0.020f; numDistortions++; // Chromatic aberration doesn't seem to change with eye relief. for ( int i = 0; i < numDistortions; i++ ) { distortions[i].Config.ChromaticAberration[0] = -0.006f; distortions[i].Config.ChromaticAberration[1] = 0.0f; distortions[i].Config.ChromaticAberration[2] = 0.014f; distortions[i].Config.ChromaticAberration[3] = 0.0f; } } else if ( hmd.EyeCups == EyeCup_PinkA || hmd.EyeCups == EyeCup_DK2A ) { // Tuned Crystal Cove & DK2 Lens (CES & GDC) numDistortions = 0; distortions[numDistortions].EyeRelief = 0.010f; distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.036f; distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10; distortions[numDistortions].Config.K[0] = 1.003f; distortions[numDistortions].Config.K[1] = 1.02f; distortions[numDistortions].Config.K[2] = 1.042f; distortions[numDistortions].Config.K[3] = 1.066f; distortions[numDistortions].Config.K[4] = 1.094f; //1.0945f; distortions[numDistortions].Config.K[5] = 1.126f; //1.127f; distortions[numDistortions].Config.K[6] = 1.162f; //1.167f; distortions[numDistortions].Config.K[7] = 1.203f; //1.218f; distortions[numDistortions].Config.K[8] = 1.25f; //1.283f; distortions[numDistortions].Config.K[9] = 1.31f; //1.37f; distortions[numDistortions].Config.K[10] = 1.38f; //1.48f; distortions[numDistortions].MaxRadius = 1.0f; distortions[numDistortions].SampleRadius[0] = 0.405405405f; distortions[numDistortions].SampleRadius[1] = 0.675675676f; distortions[numDistortions].SampleRadius[2] = 0.945945946f; defaultDistortion = numDistortions; // this is the default numDistortions++; distortions[numDistortions] = distortions[0]; distortions[numDistortions].EyeRelief = 0.020f; numDistortions++; // Chromatic aberration doesn't seem to change with eye relief. for ( int i = 0; i < numDistortions; i++ ) { distortions[i].Config.ChromaticAberration[0] = -0.015f; distortions[i].Config.ChromaticAberration[1] = -0.02f; distortions[i].Config.ChromaticAberration[2] = 0.025f; distortions[i].Config.ChromaticAberration[3] = 0.02f; } } else { // Unknown lens. // Use DK1 black lens settings, just so we can continue to run with something. distortions[0].EyeRelief = 0.005f; distortions[0].Config.MetersPerTanAngleAtCenter = 0.043875f; distortions[0].Config.Eqn = Distortion_RecipPoly4; distortions[0].Config.K[0] = 1.0f; distortions[0].Config.K[1] = -0.3999f; distortions[0].Config.K[2] = 0.2408f; distortions[0].Config.K[3] = -0.4589f; distortions[0].SampleRadius[0] = 0.2f; distortions[0].SampleRadius[1] = 0.4f; distortions[0].SampleRadius[2] = 0.6f; distortions[1] = distortions[0]; distortions[1].EyeRelief = 0.010f; numDistortions = 2; // Chromatic aberration doesn't seem to change with eye relief. for ( int i = 0; i < numDistortions; i++ ) { // These are placeholder, they have not been tuned! distortions[i].Config.ChromaticAberration[0] = 0.0f; distortions[i].Config.ChromaticAberration[1] = 0.0f; distortions[i].Config.ChromaticAberration[2] = 0.0f; distortions[i].Config.ChromaticAberration[3] = 0.0f; } } OVR_ASSERT ( numDistortions < (sizeof(distortions)/sizeof(distortions[0])) ); DistortionDescriptor *pUpper = NULL; DistortionDescriptor *pLower = NULL; float lerpVal = 0.0f; if (eyeReliefInMeters == 0) { // Use a constant default distortion if an invalid eye-relief is supplied pLower = &(distortions[defaultDistortion]); pUpper = &(distortions[defaultDistortion]); lerpVal = 0.0f; } else { for ( int i = 0; i < numDistortions-1; i++ ) { OVR_ASSERT ( distortions[i].EyeRelief < distortions[i+1].EyeRelief ); if ( ( distortions[i].EyeRelief <= eyeReliefInMeters ) && ( distortions[i+1].EyeRelief > eyeReliefInMeters ) ) { pLower = &(distortions[i]); pUpper = &(distortions[i+1]); lerpVal = ( eyeReliefInMeters - pLower->EyeRelief ) / ( pUpper->EyeRelief - pLower->EyeRelief ); // No break here - I want the ASSERT to check everything every time! } } } if ( pUpper == NULL ) { #if 0 // Outside the range, so extrapolate rather than interpolate. if ( distortions[0].EyeRelief > eyeReliefInMeters ) { pLower = &(distortions[0]); pUpper = &(distortions[1]); } else { OVR_ASSERT ( distortions[numDistortions-1].EyeRelief <= eyeReliefInMeters ); pLower = &(distortions[numDistortions-2]); pUpper = &(distortions[numDistortions-1]); } lerpVal = ( eyeReliefInMeters - pLower->EyeRelief ) / ( pUpper->EyeRelief - pLower->EyeRelief ); #else // Do not extrapolate, just clamp - slightly worried about people putting in bogus settings. if ( distortions[0].EyeRelief > eyeReliefInMeters ) { pLower = &(distortions[0]); pUpper = &(distortions[0]); } else { OVR_ASSERT ( distortions[numDistortions-1].EyeRelief <= eyeReliefInMeters ); pLower = &(distortions[numDistortions-1]); pUpper = &(distortions[numDistortions-1]); } lerpVal = 0.0f; #endif } float invLerpVal = 1.0f - lerpVal; pLower->Config.MaxR = pLower->MaxRadius; pUpper->Config.MaxR = pUpper->MaxRadius; LensConfig result; // Where is the edge of the lens - no point modelling further than this. float maxValidRadius = invLerpVal * pLower->MaxRadius + lerpVal * pUpper->MaxRadius; result.MaxR = maxValidRadius; switch ( distortionType ) { case Distortion_Poly4: // Deprecated OVR_ASSERT ( false ); break; case Distortion_RecipPoly4:{ // Lerp control points and fit an equation to them. float fitX[4]; float fitY[4]; fitX[0] = 0.0f; fitY[0] = 1.0f; for ( int ctrlPt = 1; ctrlPt < 4; ctrlPt ++ ) { float radiusLerp = invLerpVal * pLower->SampleRadius[ctrlPt-1] + lerpVal * pUpper->SampleRadius[ctrlPt-1]; float radiusLerpSq = radiusLerp * radiusLerp; float fitYLower = pLower->Config.DistortionFnScaleRadiusSquared ( radiusLerpSq ); float fitYUpper = pUpper->Config.DistortionFnScaleRadiusSquared ( radiusLerpSq ); fitX[ctrlPt] = radiusLerpSq; fitY[ctrlPt] = 1.0f / ( invLerpVal * fitYLower + lerpVal * fitYUpper ); } result.Eqn = Distortion_RecipPoly4; bool bSuccess = FitCubicPolynomial ( result.K, fitX, fitY ); OVR_ASSERT ( bSuccess ); OVR_UNUSED ( bSuccess ); // Set up the fast inverse. float maxRDist = result.DistortionFn ( maxValidRadius ); result.MaxInvR = maxRDist; result.SetUpInverseApprox(); }break; case Distortion_CatmullRom10:{ // Evenly sample & lerp points on the curve. const int NumSegments = LensConfig::NumCoefficients; result.MaxR = maxValidRadius; // Directly interpolate the K0 values result.K[0] = invLerpVal * pLower->Config.K[0] + lerpVal * pUpper->Config.K[0]; // Sample and interpolate the distortion curves to derive K[1] ... K[n] for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ ) { float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius; float fitYLower = pLower->Config.DistortionFnScaleRadiusSquared ( radiusSq ); float fitYUpper = pUpper->Config.DistortionFnScaleRadiusSquared ( radiusSq ); float fitLerp = invLerpVal * fitYLower + lerpVal * fitYUpper; result.K[ctrlPt] = fitLerp; } result.Eqn = Distortion_CatmullRom10; for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ ) { float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius; float val = result.DistortionFnScaleRadiusSquared ( radiusSq ); OVR_ASSERT ( Alg::Abs ( val - result.K[ctrlPt] ) < 0.0001f ); OVR_UNUSED1(val); // For release build. } // Set up the fast inverse. float maxRDist = result.DistortionFn ( maxValidRadius ); result.MaxInvR = maxRDist; result.SetUpInverseApprox(); }break; default: OVR_ASSERT ( false ); break; } // Chromatic aberration. result.ChromaticAberration[0] = invLerpVal * pLower->Config.ChromaticAberration[0] + lerpVal * pUpper->Config.ChromaticAberration[0]; result.ChromaticAberration[1] = invLerpVal * pLower->Config.ChromaticAberration[1] + lerpVal * pUpper->Config.ChromaticAberration[1]; result.ChromaticAberration[2] = invLerpVal * pLower->Config.ChromaticAberration[2] + lerpVal * pUpper->Config.ChromaticAberration[2]; result.ChromaticAberration[3] = invLerpVal * pLower->Config.ChromaticAberration[3] + lerpVal * pUpper->Config.ChromaticAberration[3]; // Scale. result.MetersPerTanAngleAtCenter = pLower->Config.MetersPerTanAngleAtCenter * invLerpVal + pUpper->Config.MetersPerTanAngleAtCenter * lerpVal; /* // Commented out - Causes ASSERT with no HMD plugged in #ifdef OVR_BUILD_DEBUG if ( distortionType == Distortion_CatmullRom10 ) { TestSaveLoadLensConfig ( result ); } #endif */ return result; } DistortionRenderDesc CalculateDistortionRenderDesc ( StereoEye eyeType, HmdRenderInfo const &hmd, const LensConfig *pLensOverride /*= NULL */ ) { // From eye relief, IPD and device characteristics, we get the distortion mapping. // This distortion does the following things: // 1. It undoes the distortion that happens at the edges of the lens. // 2. It maps the undistorted field into "retina" space. // So the input is a pixel coordinate - the physical pixel on the display itself. // The output is the real-world direction of the ray from this pixel as it comes out of the lens and hits the eye. // However we typically think of rays "coming from" the eye, so the direction (TanAngleX,TanAngleY,1) is the direction // that the pixel appears to be in real-world space, where AngleX and AngleY are relative to the straight-ahead vector. // If your renderer is a raytracer, you can use this vector directly (normalize as appropriate). // However in standard rasterisers, we have rendered a 2D image and are putting it in front of the eye, // so we then need a mapping from this space to the [-1,1] UV coordinate space, which depends on exactly // where "in space" the app wants to put that rendertarget. // Where in space, and how large this rendertarget is, is completely up to the app and/or user, // though of course we can provide some useful hints. // TODO: Use IPD and eye relief to modify distortion (i.e. non-radial component) // TODO: cope with lenses that don't produce collimated light. // This means that IPD relative to the lens separation changes the light vergence, // and so we actually need to change where the image is displayed. const HmdRenderInfo::EyeConfig &hmdEyeConfig = ( eyeType == StereoEye_Left ) ? hmd.EyeLeft : hmd.EyeRight; DistortionRenderDesc localDistortion; localDistortion.Lens = hmdEyeConfig.Distortion; if ( pLensOverride != NULL ) { localDistortion.Lens = *pLensOverride; } Sizef pixelsPerMeter(hmd.ResolutionInPixels.w / ( hmd.ScreenSizeInMeters.w - hmd.ScreenGapSizeInMeters ), hmd.ResolutionInPixels.h / hmd.ScreenSizeInMeters.h); localDistortion.PixelsPerTanAngleAtCenter = (pixelsPerMeter * localDistortion.Lens.MetersPerTanAngleAtCenter).ToVector(); // Same thing, scaled to [-1,1] for each eye, rather than pixels. localDistortion.TanEyeAngleScale = Vector2f(0.25f, 0.5f).EntrywiseMultiply( (hmd.ScreenSizeInMeters / localDistortion.Lens.MetersPerTanAngleAtCenter).ToVector()); // <--------------left eye------------------><-ScreenGapSizeInMeters-><--------------right eye-----------------> // <------------------------------------------ScreenSizeInMeters.Width-----------------------------------------> // <----------------LensSeparationInMeters---------------> // <--centerFromLeftInMeters-> // ^ // Center of lens // Find the lens centers in scale of [-1,+1] (NDC) in left eye. float visibleWidthOfOneEye = 0.5f * ( hmd.ScreenSizeInMeters.w - hmd.ScreenGapSizeInMeters ); float centerFromLeftInMeters = ( hmd.ScreenSizeInMeters.w - hmd.LensSeparationInMeters ) * 0.5f; localDistortion.LensCenter.x = ( centerFromLeftInMeters / visibleWidthOfOneEye ) * 2.0f - 1.0f; localDistortion.LensCenter.y = ( hmd.CenterFromTopInMeters / hmd.ScreenSizeInMeters.h ) * 2.0f - 1.0f; if ( eyeType == StereoEye_Right ) { localDistortion.LensCenter.x = -localDistortion.LensCenter.x; } return localDistortion; } FovPort CalculateFovFromEyePosition ( float eyeReliefInMeters, float offsetToRightInMeters, float offsetDownwardsInMeters, float lensDiameterInMeters, float extraEyeRotationInRadians /*= 0.0f*/ ) { // 2D view of things: // |-| <--- offsetToRightInMeters (in this case, it is negative) // |=======C=======| <--- lens surface (C=center) // \ | _/ // \ R _/ // \ | _/ // \ | _/ // \|/ // O <--- center of pupil // (technically the lens is round rather than square, so it's not correct to // separate vertical and horizontal like this, but it's close enough) float halfLensDiameter = lensDiameterInMeters * 0.5f; FovPort fovPort; fovPort.UpTan = ( halfLensDiameter + offsetDownwardsInMeters ) / eyeReliefInMeters; fovPort.DownTan = ( halfLensDiameter - offsetDownwardsInMeters ) / eyeReliefInMeters; fovPort.LeftTan = ( halfLensDiameter + offsetToRightInMeters ) / eyeReliefInMeters; fovPort.RightTan = ( halfLensDiameter - offsetToRightInMeters ) / eyeReliefInMeters; if ( extraEyeRotationInRadians > 0.0f ) { // That's the basic looking-straight-ahead eye position relative to the lens. // But if you look left, the pupil moves left as the eyeball rotates, which // means you can see more to the right than this geometry suggests. // So add in the bounds for the extra movement of the pupil. // Beyond 30 degrees does not increase FOV because the pupil starts moving backwards more than sideways. extraEyeRotationInRadians = Alg::Min ( DegreeToRad ( 30.0f ), Alg::Max ( 0.0f, extraEyeRotationInRadians ) ); // The rotation of the eye is a bit more complex than a simple circle. The center of rotation // at 13.5mm from cornea is slightly further back than the actual center of the eye. // Additionally the rotation contains a small lateral component as the muscles pull the eye const float eyeballCenterToPupil = 0.0135f; // center of eye rotation const float eyeballLateralPull = 0.001f * (extraEyeRotationInRadians / DegreeToRad ( 30.0f)); // lateral motion as linear function float extraTranslation = eyeballCenterToPupil * sinf ( extraEyeRotationInRadians ) + eyeballLateralPull; float extraRelief = eyeballCenterToPupil * ( 1.0f - cosf ( extraEyeRotationInRadians ) ); fovPort.UpTan = Alg::Max ( fovPort.UpTan , ( halfLensDiameter + offsetDownwardsInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) ); fovPort.DownTan = Alg::Max ( fovPort.DownTan , ( halfLensDiameter - offsetDownwardsInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) ); fovPort.LeftTan = Alg::Max ( fovPort.LeftTan , ( halfLensDiameter + offsetToRightInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) ); fovPort.RightTan = Alg::Max ( fovPort.RightTan, ( halfLensDiameter - offsetToRightInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) ); } return fovPort; } FovPort CalculateFovFromHmdInfo ( StereoEye eyeType, DistortionRenderDesc const &distortion, HmdRenderInfo const &hmd, float extraEyeRotationInRadians /*= 0.0f*/ ) { FovPort fovPort; float eyeReliefInMeters; float offsetToRightInMeters; if ( eyeType == StereoEye_Right ) { eyeReliefInMeters = hmd.EyeRight.ReliefInMeters; offsetToRightInMeters = hmd.EyeRight.NoseToPupilInMeters - 0.5f * hmd.LensSeparationInMeters; } else { eyeReliefInMeters = hmd.EyeLeft.ReliefInMeters; offsetToRightInMeters = -(hmd.EyeLeft.NoseToPupilInMeters - 0.5f * hmd.LensSeparationInMeters); } // Limit the eye-relief to 6 mm for FOV calculations since this just tends to spread off-screen // and get clamped anyways on DK1 (but in Unity it continues to spreads and causes // unnecessarily large render targets) eyeReliefInMeters = Alg::Max(eyeReliefInMeters, 0.006f); // Central view. fovPort = CalculateFovFromEyePosition ( eyeReliefInMeters, offsetToRightInMeters, 0.0f, hmd.LensDiameterInMeters, extraEyeRotationInRadians ); // clamp to the screen fovPort = ClampToPhysicalScreenFov ( eyeType, distortion, fovPort ); return fovPort; } FovPort GetPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion ) { OVR_UNUSED1 ( eyeType ); FovPort resultFovPort; // Figure out the boundaries of the screen. We take the middle pixel of the screen, // move to each of the four screen edges, and transform those back into TanAngle space. Vector2f dmiddle = distortion.LensCenter; // The gotcha is that for some distortion functions, the map will "wrap around" // for screen pixels that are not actually visible to the user (especially on DK1, // which has a lot of invisible pixels), and map to pixels that are close to the middle. // This means the edges of the screen will actually be // "closer" than the visible bounds, so we'll clip too aggressively. // Solution - step gradually towards the boundary, noting the maximum distance. struct FunctionHider { static FovPort FindRange ( Vector2f from, Vector2f to, int numSteps, DistortionRenderDesc const &distortion ) { FovPort result; result.UpTan = 0.0f; result.DownTan = 0.0f; result.LeftTan = 0.0f; result.RightTan = 0.0f; float stepScale = 1.0f / ( numSteps - 1 ); for ( int step = 0; step < numSteps; step++ ) { float lerpFactor = stepScale * (float)step; Vector2f sample = from + (to - from) * lerpFactor; Vector2f tanEyeAngle = TransformScreenNDCToTanFovSpace ( distortion, sample ); result.LeftTan = Alg::Max ( result.LeftTan, -tanEyeAngle.x ); result.RightTan = Alg::Max ( result.RightTan, tanEyeAngle.x ); result.UpTan = Alg::Max ( result.UpTan, -tanEyeAngle.y ); result.DownTan = Alg::Max ( result.DownTan, tanEyeAngle.y ); } return result; } }; FovPort leftFovPort = FunctionHider::FindRange( dmiddle, Vector2f( -1.0f, dmiddle.y ), 10, distortion ); FovPort rightFovPort = FunctionHider::FindRange( dmiddle, Vector2f( 1.0f, dmiddle.y ), 10, distortion ); FovPort upFovPort = FunctionHider::FindRange( dmiddle, Vector2f( dmiddle.x, -1.0f ), 10, distortion ); FovPort downFovPort = FunctionHider::FindRange( dmiddle, Vector2f( dmiddle.x, 1.0f ), 10, distortion ); resultFovPort.LeftTan = leftFovPort.LeftTan; resultFovPort.RightTan = rightFovPort.RightTan; resultFovPort.UpTan = upFovPort.UpTan; resultFovPort.DownTan = downFovPort.DownTan; return resultFovPort; } FovPort ClampToPhysicalScreenFov( StereoEye eyeType, DistortionRenderDesc const &distortion, FovPort inputFovPort ) { FovPort resultFovPort; FovPort phsyicalFovPort = GetPhysicalScreenFov ( eyeType, distortion ); resultFovPort.LeftTan = Alg::Min ( inputFovPort.LeftTan, phsyicalFovPort.LeftTan ); resultFovPort.RightTan = Alg::Min ( inputFovPort.RightTan, phsyicalFovPort.RightTan ); resultFovPort.UpTan = Alg::Min ( inputFovPort.UpTan, phsyicalFovPort.UpTan ); resultFovPort.DownTan = Alg::Min ( inputFovPort.DownTan, phsyicalFovPort.DownTan ); return resultFovPort; } Sizei CalculateIdealPixelSize ( StereoEye eyeType, DistortionRenderDesc const &distortion, FovPort tanHalfFov, float pixelsPerDisplayPixel ) { OVR_UNUSED(eyeType); // might be useful in the future if we do overlapping fovs Sizei result; // TODO: if the app passes in a FOV that doesn't cover the centre, use the distortion values for the nearest edge/corner to match pixel size. result.w = (int)(0.5f + pixelsPerDisplayPixel * distortion.PixelsPerTanAngleAtCenter.x * ( tanHalfFov.LeftTan + tanHalfFov.RightTan ) ); result.h = (int)(0.5f + pixelsPerDisplayPixel * distortion.PixelsPerTanAngleAtCenter.y * ( tanHalfFov.UpTan + tanHalfFov.DownTan ) ); return result; } Recti GetFramebufferViewport ( StereoEye eyeType, HmdRenderInfo const &hmd ) { Recti result; result.w = hmd.ResolutionInPixels.w/2; result.h = hmd.ResolutionInPixels.h; result.x = 0; result.y = 0; if ( eyeType == StereoEye_Right ) { result.x = (hmd.ResolutionInPixels.w+1)/2; // Round up, not down. } return result; } ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort tanHalfFov ) { float projXScale = 2.0f / ( tanHalfFov.LeftTan + tanHalfFov.RightTan ); float projXOffset = ( tanHalfFov.LeftTan - tanHalfFov.RightTan ) * projXScale * 0.5f; float projYScale = 2.0f / ( tanHalfFov.UpTan + tanHalfFov.DownTan ); float projYOffset = ( tanHalfFov.UpTan - tanHalfFov.DownTan ) * projYScale * 0.5f; ScaleAndOffset2D result; result.Scale = Vector2f(projXScale, projYScale); result.Offset = Vector2f(projXOffset, projYOffset); // Hey - why is that Y.Offset negated? // It's because a projection matrix transforms from world coords with Y=up, // whereas this is from NDC which is Y=down. return result; } ScaleAndOffset2D CreateUVScaleAndOffsetfromNDCScaleandOffset ( ScaleAndOffset2D scaleAndOffsetNDC, Recti renderedViewport, Sizei renderTargetSize ) { // scaleAndOffsetNDC takes you to NDC space [-1,+1] within the given viewport on the rendertarget. // We want a scale to instead go to actual UV coordinates you can sample with, // which need [0,1] and ignore the viewport. ScaleAndOffset2D result; // Scale [-1,1] to [0,1] result.Scale = scaleAndOffsetNDC.Scale * 0.5f; result.Offset = scaleAndOffsetNDC.Offset * 0.5f + Vector2f(0.5f); // ...but we will have rendered to a subsection of the RT, so scale for that. Vector2f scale( (float)renderedViewport.w / (float)renderTargetSize.w, (float)renderedViewport.h / (float)renderTargetSize.h ); Vector2f offset( (float)renderedViewport.x / (float)renderTargetSize.w, (float)renderedViewport.y / (float)renderTargetSize.h ); result.Scale = result.Scale.EntrywiseMultiply(scale); result.Offset = result.Offset.EntrywiseMultiply(scale) + offset; return result; } Matrix4f CreateProjection( bool rightHanded, FovPort tanHalfFov, float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/ ) { // A projection matrix is very like a scaling from NDC, so we can start with that. ScaleAndOffset2D scaleAndOffset = CreateNDCScaleAndOffsetFromFov ( tanHalfFov ); float handednessScale = 1.0f; if ( rightHanded ) { handednessScale = -1.0f; } Matrix4f projection; // Produces X result, mapping clip edges to [-w,+w] projection.M[0][0] = scaleAndOffset.Scale.x; projection.M[0][1] = 0.0f; projection.M[0][2] = handednessScale * scaleAndOffset.Offset.x; projection.M[0][3] = 0.0f; // Produces Y result, mapping clip edges to [-w,+w] // Hey - why is that YOffset negated? // It's because a projection matrix transforms from world coords with Y=up, // whereas this is derived from an NDC scaling, which is Y=down. projection.M[1][0] = 0.0f; projection.M[1][1] = scaleAndOffset.Scale.y; projection.M[1][2] = handednessScale * -scaleAndOffset.Offset.y; projection.M[1][3] = 0.0f; // Produces Z-buffer result - app needs to fill this in with whatever Z range it wants. // We'll just use some defaults for now. projection.M[2][0] = 0.0f; projection.M[2][1] = 0.0f; projection.M[2][2] = -handednessScale * zFar / (zNear - zFar); projection.M[2][3] = (zFar * zNear) / (zNear - zFar); // Produces W result (= Z in) projection.M[3][0] = 0.0f; projection.M[3][1] = 0.0f; projection.M[3][2] = handednessScale; projection.M[3][3] = 0.0f; return projection; } Matrix4f CreateOrthoSubProjection ( bool rightHanded, StereoEye eyeType, float tanHalfFovX, float tanHalfFovY, float unitsX, float unitsY, float distanceFromCamera, float interpupillaryDistance, Matrix4f const &projection, float zNear /*= 0.0f*/, float zFar /*= 0.0f*/ ) { OVR_UNUSED1 ( rightHanded ); float orthoHorizontalOffset = interpupillaryDistance * 0.5f / distanceFromCamera; switch ( eyeType ) { case StereoEye_Center: orthoHorizontalOffset = 0.0f; break; case StereoEye_Left: break; case StereoEye_Right: orthoHorizontalOffset = -orthoHorizontalOffset; break; default: OVR_ASSERT ( false ); break; } // Current projection maps real-world vector (x,y,1) to the RT. // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to // the physical [-orthoHalfFov,orthoHalfFov] // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means // we don't have to feed in Z=1 all the time. // The horizontal offset math is a little hinky because the destination is // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset] // So we need to first map [-FovPixels/2,FovPixels/2] to // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]: // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset; // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset; // But then we need the sam mapping as the existing projection matrix, i.e. // x2 = x1 * Projection.M[0][0] + Projection.M[0][2]; // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2]; // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels + // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]; // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]. float orthoScaleX = 2.0f * tanHalfFovX / unitsX; float orthoScaleY = 2.0f * tanHalfFovY / unitsY; Matrix4f ortho; ortho.M[0][0] = projection.M[0][0] * orthoScaleX; ortho.M[0][1] = 0.0f; ortho.M[0][2] = 0.0f; ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] ); ortho.M[1][0] = 0.0f; ortho.M[1][1] = -projection.M[1][1] * orthoScaleY; // Note sign flip (text rendering uses Y=down). ortho.M[1][2] = 0.0f; ortho.M[1][3] = -projection.M[1][2]; if ( fabsf ( zNear - zFar ) < 0.001f ) { ortho.M[2][0] = 0.0f; ortho.M[2][1] = 0.0f; ortho.M[2][2] = 0.0f; ortho.M[2][3] = zFar; } else { ortho.M[2][0] = 0.0f; ortho.M[2][1] = 0.0f; ortho.M[2][2] = zFar / (zNear - zFar); ortho.M[2][3] = (zFar * zNear) / (zNear - zFar); } // No perspective correction for ortho. ortho.M[3][0] = 0.0f; ortho.M[3][1] = 0.0f; ortho.M[3][2] = 0.0f; ortho.M[3][3] = 1.0f; return ortho; } //----------------------------------------------------------------------------------- // A set of "forward-mapping" functions, mapping from framebuffer space to real-world and/or texture space. // This mimics the first half of the distortion shader's function. Vector2f TransformScreenNDCToTanFovSpace( DistortionRenderDesc const &distortion, const Vector2f &framebufferNDC ) { // Scale to TanHalfFov space, but still distorted. Vector2f tanEyeAngleDistorted; tanEyeAngleDistorted.x = ( framebufferNDC.x - distortion.LensCenter.x ) * distortion.TanEyeAngleScale.x; tanEyeAngleDistorted.y = ( framebufferNDC.y - distortion.LensCenter.y ) * distortion.TanEyeAngleScale.y; // Distort. float radiusSquared = ( tanEyeAngleDistorted.x * tanEyeAngleDistorted.x ) + ( tanEyeAngleDistorted.y * tanEyeAngleDistorted.y ); float distortionScale = distortion.Lens.DistortionFnScaleRadiusSquared ( radiusSquared ); Vector2f tanEyeAngle; tanEyeAngle.x = tanEyeAngleDistorted.x * distortionScale; tanEyeAngle.y = tanEyeAngleDistorted.y * distortionScale; return tanEyeAngle; } // Same, with chromatic aberration correction. void TransformScreenNDCToTanFovSpaceChroma ( Vector2f *resultR, Vector2f *resultG, Vector2f *resultB, DistortionRenderDesc const &distortion, const Vector2f &framebufferNDC ) { // Scale to TanHalfFov space, but still distorted. Vector2f tanEyeAngleDistorted; tanEyeAngleDistorted.x = ( framebufferNDC.x - distortion.LensCenter.x ) * distortion.TanEyeAngleScale.x; tanEyeAngleDistorted.y = ( framebufferNDC.y - distortion.LensCenter.y ) * distortion.TanEyeAngleScale.y; // Distort. float radiusSquared = ( tanEyeAngleDistorted.x * tanEyeAngleDistorted.x ) + ( tanEyeAngleDistorted.y * tanEyeAngleDistorted.y ); Vector3f distortionScales = distortion.Lens.DistortionFnScaleRadiusSquaredChroma ( radiusSquared ); *resultR = tanEyeAngleDistorted * distortionScales.x; *resultG = tanEyeAngleDistorted * distortionScales.y; *resultB = tanEyeAngleDistorted * distortionScales.z; } // This mimics the second half of the distortion shader's function. Vector2f TransformTanFovSpaceToRendertargetTexUV( StereoEyeParams const &eyeParams, Vector2f const &tanEyeAngle ) { Vector2f textureUV; textureUV.x = tanEyeAngle.x * eyeParams.EyeToSourceUV.Scale.x + eyeParams.EyeToSourceUV.Offset.x; textureUV.y = tanEyeAngle.y * eyeParams.EyeToSourceUV.Scale.y + eyeParams.EyeToSourceUV.Offset.y; return textureUV; } Vector2f TransformTanFovSpaceToRendertargetNDC( StereoEyeParams const &eyeParams, Vector2f const &tanEyeAngle ) { Vector2f textureNDC; textureNDC.x = tanEyeAngle.x * eyeParams.EyeToSourceNDC.Scale.x + eyeParams.EyeToSourceNDC.Offset.x; textureNDC.y = tanEyeAngle.y * eyeParams.EyeToSourceNDC.Scale.y + eyeParams.EyeToSourceNDC.Offset.y; return textureNDC; } Vector2f TransformScreenPixelToScreenNDC( Recti const &distortionViewport, Vector2f const &pixel ) { // Move to [-1,1] NDC coords. Vector2f framebufferNDC; framebufferNDC.x = -1.0f + 2.0f * ( ( pixel.x - (float)distortionViewport.x ) / (float)distortionViewport.w ); framebufferNDC.y = -1.0f + 2.0f * ( ( pixel.y - (float)distortionViewport.y ) / (float)distortionViewport.h ); return framebufferNDC; } Vector2f TransformScreenPixelToTanFovSpace( Recti const &distortionViewport, DistortionRenderDesc const &distortion, Vector2f const &pixel ) { return TransformScreenNDCToTanFovSpace( distortion, TransformScreenPixelToScreenNDC( distortionViewport, pixel ) ); } Vector2f TransformScreenNDCToRendertargetTexUV( DistortionRenderDesc const &distortion, StereoEyeParams const &eyeParams, Vector2f const &pixel ) { return TransformTanFovSpaceToRendertargetTexUV ( eyeParams, TransformScreenNDCToTanFovSpace ( distortion, pixel ) ); } Vector2f TransformScreenPixelToRendertargetTexUV( Recti const &distortionViewport, DistortionRenderDesc const &distortion, StereoEyeParams const &eyeParams, Vector2f const &pixel ) { return TransformTanFovSpaceToRendertargetTexUV ( eyeParams, TransformScreenPixelToTanFovSpace ( distortionViewport, distortion, pixel ) ); } //----------------------------------------------------------------------------------- // A set of "reverse-mapping" functions, mapping from real-world and/or texture space back to the framebuffer. Vector2f TransformTanFovSpaceToScreenNDC( DistortionRenderDesc const &distortion, const Vector2f &tanEyeAngle, bool usePolyApprox /*= false*/ ) { float tanEyeAngleRadius = tanEyeAngle.Length(); float tanEyeAngleDistortedRadius = distortion.Lens.DistortionFnInverseApprox ( tanEyeAngleRadius ); if ( !usePolyApprox ) { tanEyeAngleDistortedRadius = distortion.Lens.DistortionFnInverse ( tanEyeAngleRadius ); } Vector2f tanEyeAngleDistorted = tanEyeAngle; if ( tanEyeAngleRadius > 0.0f ) { tanEyeAngleDistorted = tanEyeAngle * ( tanEyeAngleDistortedRadius / tanEyeAngleRadius ); } Vector2f framebufferNDC; framebufferNDC.x = ( tanEyeAngleDistorted.x / distortion.TanEyeAngleScale.x ) + distortion.LensCenter.x; framebufferNDC.y = ( tanEyeAngleDistorted.y / distortion.TanEyeAngleScale.y ) + distortion.LensCenter.y; return framebufferNDC; } Vector2f TransformRendertargetNDCToTanFovSpace( const ScaleAndOffset2D &eyeToSourceNDC, const Vector2f &textureNDC ) { Vector2f tanEyeAngle = (textureNDC - eyeToSourceNDC.Offset) / eyeToSourceNDC.Scale; return tanEyeAngle; } } //namespace OVR //Just want to make a copy disentangled from all these namespaces! float ExtEvalCatmullRom10Spline ( float const *K, float scaledVal ) { return(OVR::EvalCatmullRom10Spline ( K, scaledVal )); } \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_Stereo.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Stereo.h new file mode 100644 index 0000000..9265444 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_Stereo.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : OVR_Stereo.h Content : Stereo rendering functions Created : November 30, 2013 Authors : Tom Fosyth Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Stereo_h #define OVR_Stereo_h #include "OVR_Device.h" // CAPI Forward declaration. typedef struct ovrFovPort_ ovrFovPort; typedef struct ovrRecti_ ovrRecti; namespace OVR { //----------------------------------------------------------------------------------- // ***** Stereo Enumerations // StereoEye specifies which eye we are rendering for; it is used to // retrieve StereoEyeParams. enum StereoEye { StereoEye_Center, StereoEye_Left, StereoEye_Right }; //----------------------------------------------------------------------------------- // ***** FovPort // FovPort describes Field Of View (FOV) of a viewport. // This class has values for up, down, left and right, stored in // tangent of the angle units to simplify calculations. // // As an example, for a standard 90 degree vertical FOV, we would // have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }. // // CreateFromRadians/Degrees helper functions can be used to // access FOV in different units. struct FovPort { float UpTan; float DownTan; float LeftTan; float RightTan; FovPort ( float sideTan = 0.0f ) : UpTan(sideTan), DownTan(sideTan), LeftTan(sideTan), RightTan(sideTan) { } FovPort ( float u, float d, float l, float r ) : UpTan(u), DownTan(d), LeftTan(l), RightTan(r) { } // C-interop support: FovPort <-> ovrFovPort (implementation in OVR_CAPI.cpp). FovPort(const ovrFovPort& src); operator ovrFovPort () const; static FovPort CreateFromRadians(float horizontalFov, float verticalFov) { FovPort result; result.UpTan = tanf ( verticalFov * 0.5f ); result.DownTan = tanf ( verticalFov * 0.5f ); result.LeftTan = tanf ( horizontalFov * 0.5f ); result.RightTan = tanf ( horizontalFov * 0.5f ); return result; } static FovPort CreateFromDegrees(float horizontalFovDegrees, float verticalFovDegrees) { return CreateFromRadians(DegreeToRad(horizontalFovDegrees), DegreeToRad(verticalFovDegrees)); } // Get Horizontal/Vertical components of Fov in radians. float GetVerticalFovRadians() const { return atanf(UpTan) + atanf(DownTan); } float GetHorizontalFovRadians() const { return atanf(LeftTan) + atanf(RightTan); } // Get Horizontal/Vertical components of Fov in degrees. float GetVerticalFovDegrees() const { return RadToDegree(GetVerticalFovRadians()); } float GetHorizontalFovDegrees() const { return RadToDegree(GetHorizontalFovRadians()); } // Compute maximum tangent value among all four sides. float GetMaxSideTan() const { return Alg::Max(Alg::Max(UpTan, DownTan), Alg::Max(LeftTan, RightTan)); } // Converts Fov Tan angle units to [-1,1] render target NDC space Vector2f TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle); // Compute per-channel minimum and maximum of Fov. static FovPort Min(const FovPort& a, const FovPort& b) { FovPort fov( Alg::Min( a.UpTan , b.UpTan ), Alg::Min( a.DownTan , b.DownTan ), Alg::Min( a.LeftTan , b.LeftTan ), Alg::Min( a.RightTan, b.RightTan ) ); return fov; } static FovPort Max(const FovPort& a, const FovPort& b) { FovPort fov( Alg::Max( a.UpTan , b.UpTan ), Alg::Max( a.DownTan , b.DownTan ), Alg::Max( a.LeftTan , b.LeftTan ), Alg::Max( a.RightTan, b.RightTan ) ); return fov; } }; //----------------------------------------------------------------------------------- // ***** ScaleAndOffset struct ScaleAndOffset2D { Vector2f Scale; Vector2f Offset; ScaleAndOffset2D(float sx = 0.0f, float sy = 0.0f, float ox = 0.0f, float oy = 0.0f) : Scale(sx, sy), Offset(ox, oy) { } }; //----------------------------------------------------------------------------------- // ***** Misc. utility functions. // Inputs are 4 points (pFitX[0],pFitY[0]) through (pFitX[3],pFitY[3]) // Result is four coefficients in pResults[0] through pResults[3] such that // y = pResult[0] + x * ( pResult[1] + x * ( pResult[2] + x * ( pResult[3] ) ) ); // passes through all four input points. // Return is true if it succeeded, false if it failed (because two control points // have the same pFitX value). bool FitCubicPolynomial ( float *pResult, const float *pFitX, const float *pFitY ); //----------------------------------------------------------------------------------- // ***** LensConfig // LensConfig describes the configuration of a single lens in an HMD. // - Eqn and K[] describe a distortion function. // - MetersPerTanAngleAtCenter is the relationship between distance on a // screen (at the center of the lens), and the angle variance of the light after it // has passed through the lens. // - ChromaticAberration is an array of parameters for controlling // additional Red and Blue scaling in order to reduce chromatic aberration // caused by the Rift lenses. struct LensConfig { // The result is a scaling applied to the distance from the center of the lens. float DistortionFnScaleRadiusSquared (float rsq) const; // x,y,z components map to r,g,b scales. Vector3f DistortionFnScaleRadiusSquaredChroma (float rsq) const; // DistortionFn applies distortion to the argument. // Input: the distance in TanAngle/NIC space from the optical center to the input pixel. // Output: the resulting distance after distortion. float DistortionFn(float r) const { return r * DistortionFnScaleRadiusSquared ( r * r ); } // DistortionFnInverse computes the inverse of the distortion function on an argument. float DistortionFnInverse(float r) const; // Also computes the inverse, but using a polynomial approximation. Warning - it's just an approximation! float DistortionFnInverseApprox(float r) const; // Sets up InvK[]. void SetUpInverseApprox(); // Sets a bunch of sensible defaults. void SetToIdentity(); enum { NumCoefficients = 11 }; DistortionEqnType Eqn; float K[NumCoefficients]; float MaxR; // The highest R you're going to query for - the curve is unpredictable beyond it. float MetersPerTanAngleAtCenter; // Additional per-channel scaling is applied after distortion: // Index [0] - Red channel constant coefficient. // Index [1] - Red channel r^2 coefficient. // Index [2] - Blue channel constant coefficient. // Index [3] - Blue channel r^2 coefficient. float ChromaticAberration[4]; float InvK[NumCoefficients]; float MaxInvR; }; // For internal use - storing and loading lens config data // Returns true on success. bool LoadLensConfig ( LensConfig *presult, UByte const *pbuffer, int bufferSizeInBytes ); // Returns number of bytes needed. int SaveLensConfigSizeInBytes ( LensConfig const &config ); // Returns true on success. bool SaveLensConfig ( UByte *pbuffer, int bufferSizeInBytes, LensConfig const &config ); //----------------------------------------------------------------------------------- // ***** DistortionRenderDesc // This describes distortion for a single eye in an HMD with a display, not just the lens by itself. struct DistortionRenderDesc { // The raw lens values. LensConfig Lens; // These map from [-1,1] across the eye being rendered into TanEyeAngle space (but still distorted) Vector2f LensCenter; Vector2f TanEyeAngleScale; // Computed from device characteristics, IPD and eye-relief. // (not directly used for rendering, but very useful) Vector2f PixelsPerTanAngleAtCenter; }; //----------------------------------------------------------------------------------- // ***** HmdRenderInfo // All the parts of the HMD info that are needed to set up the rendering system. struct HmdRenderInfo { // The start of this sturucture is intentionally very similar to HMDInfo in OVER_Device.h // However to reduce interdependencies, one does not simply #include the other. HmdTypeEnum HmdType; // Size of the entire screen Size ResolutionInPixels; Size ScreenSizeInMeters; float ScreenGapSizeInMeters; // Characteristics of the lenses. float CenterFromTopInMeters; float LensSeparationInMeters; float LensDiameterInMeters; float LensSurfaceToMidplateInMeters; EyeCupType EyeCups; // Timing & shutter data. All values in seconds. struct ShutterInfo { HmdShutterTypeEnum Type; float VsyncToNextVsync; // 1/framerate float VsyncToFirstScanline; // for global shutter, vsync->shutter open. float FirstScanlineToLastScanline; // for global shutter, will be zero. float PixelSettleTime; // estimated. float PixelPersistence; // Full persistence = 1/framerate. } Shutter; // These are all set from the user's profile. struct EyeConfig { // Distance from center of eyeball to front plane of lens. float ReliefInMeters; // Distance from nose (technically, center of Rift) to the middle of the eye. float NoseToPupilInMeters; LensConfig Distortion; } EyeLeft, EyeRight; HmdRenderInfo() { HmdType = HmdType_None; ResolutionInPixels.w = 0; ResolutionInPixels.h = 0; ScreenSizeInMeters.w = 0.0f; ScreenSizeInMeters.h = 0.0f; ScreenGapSizeInMeters = 0.0f; CenterFromTopInMeters = 0.0f; LensSeparationInMeters = 0.0f; LensDiameterInMeters = 0.0f; LensSurfaceToMidplateInMeters = 0.0f; Shutter.Type = HmdShutter_LAST; Shutter.VsyncToNextVsync = 0.0f; Shutter.VsyncToFirstScanline = 0.0f; Shutter.FirstScanlineToLastScanline = 0.0f; Shutter.PixelSettleTime = 0.0f; Shutter.PixelPersistence = 0.0f; EyeCups = EyeCup_DK1A; EyeLeft.ReliefInMeters = 0.0f; EyeLeft.NoseToPupilInMeters = 0.0f; EyeLeft.Distortion.SetToIdentity(); EyeRight = EyeLeft; } // The "center eye" is the position the HMD tracking returns, // and games will also usually use it for audio, aiming reticles, some line-of-sight tests, etc. EyeConfig GetEyeCenter() const { EyeConfig result; result.ReliefInMeters = 0.5f * ( EyeLeft.ReliefInMeters + EyeRight.ReliefInMeters ); result.NoseToPupilInMeters = 0.0f; result.Distortion.SetToIdentity(); return result; } }; //----------------------------------------------------------------------------------- // Stateless computation functions, in somewhat recommended execution order. // For examples on how to use many of them, see the StereoConfig::UpdateComputedState function. const float OVR_DEFAULT_EXTRA_EYE_ROTATION = 30.0f * Math::DegreeToRadFactor; // Creates a dummy debug HMDInfo matching a particular HMD model. // Useful for development without an actual HMD attached. HMDInfo CreateDebugHMDInfo(HmdTypeEnum hmdType); // profile may be NULL, in which case it uses the hard-coded defaults. // distortionType should be left at the default unless you require something specific for your distortion shaders. // eyeCupOverride can be EyeCup_LAST, in which case it uses the one in the profile. HmdRenderInfo GenerateHmdRenderInfoFromHmdInfo ( HMDInfo const &hmdInfo, Profile const *profile = NULL, DistortionEqnType distortionType = Distortion_CatmullRom10, EyeCupType eyeCupOverride = EyeCup_LAST ); LensConfig GenerateLensConfigFromEyeRelief ( float eyeReliefInMeters, HmdRenderInfo const &hmd, DistortionEqnType distortionType = Distortion_CatmullRom10 ); DistortionRenderDesc CalculateDistortionRenderDesc ( StereoEye eyeType, HmdRenderInfo const &hmd, LensConfig const *pLensOverride = NULL ); FovPort CalculateFovFromEyePosition ( float eyeReliefInMeters, float offsetToRightInMeters, float offsetDownwardsInMeters, float lensDiameterInMeters, float extraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION); FovPort CalculateFovFromHmdInfo ( StereoEye eyeType, DistortionRenderDesc const &distortion, HmdRenderInfo const &hmd, float extraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION ); FovPort GetPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion ); FovPort ClampToPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion, FovPort inputFovPort ); Sizei CalculateIdealPixelSize ( StereoEye eyeType, DistortionRenderDesc const &distortion, FovPort fov, float pixelsPerDisplayPixel ); Recti GetFramebufferViewport ( StereoEye eyeType, HmdRenderInfo const &hmd ); Matrix4f CreateProjection ( bool rightHanded, FovPort fov, float zNear = 0.01f, float zFar = 10000.0f ); Matrix4f CreateOrthoSubProjection ( bool rightHanded, StereoEye eyeType, float tanHalfFovX, float tanHalfFovY, float unitsX, float unitsY, float distanceFromCamera, float interpupillaryDistance, Matrix4f const &projection, float zNear = 0.0f, float zFar = 0.0f ); ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort fov ); ScaleAndOffset2D CreateUVScaleAndOffsetfromNDCScaleandOffset ( ScaleAndOffset2D scaleAndOffsetNDC, Recti renderedViewport, Sizei renderTargetSize ); //----------------------------------------------------------------------------------- // ***** StereoEyeParams // StereoEyeParams describes RenderDevice configuration needed to render // the scene for one eye. struct StereoEyeParams { StereoEye Eye; Matrix4f ViewAdjust; // Translation to be applied to view matrix. // Distortion and the VP on the physical display - the thing to run the distortion shader on. DistortionRenderDesc Distortion; Recti DistortionViewport; // Projection and VP of a particular view (you could have multiple of these). Recti RenderedViewport; // Viewport that we render the standard scene to. FovPort Fov; // The FOVs of this scene. Matrix4f RenderedProjection; // Projection matrix used with this eye. ScaleAndOffset2D EyeToSourceNDC; // Mapping from TanEyeAngle space to [-1,+1] on the rendered image. ScaleAndOffset2D EyeToSourceUV; // Mapping from TanEyeAngle space to actual texture UV coords. }; //----------------------------------------------------------------------------------- // A set of "forward-mapping" functions, mapping from framebuffer space to real-world and/or texture space. Vector2f TransformScreenNDCToTanFovSpace ( DistortionRenderDesc const &distortion, const Vector2f &framebufferNDC ); void TransformScreenNDCToTanFovSpaceChroma ( Vector2f *resultR, Vector2f *resultG, Vector2f *resultB, DistortionRenderDesc const &distortion, const Vector2f &framebufferNDC ); Vector2f TransformTanFovSpaceToRendertargetTexUV ( StereoEyeParams const &eyeParams, Vector2f const &tanEyeAngle ); Vector2f TransformTanFovSpaceToRendertargetNDC ( StereoEyeParams const &eyeParams, Vector2f const &tanEyeAngle ); Vector2f TransformScreenPixelToScreenNDC( Recti const &distortionViewport, Vector2f const &pixel ); Vector2f TransformScreenPixelToTanFovSpace ( Recti const &distortionViewport, DistortionRenderDesc const &distortion, Vector2f const &pixel ); Vector2f TransformScreenNDCToRendertargetTexUV( DistortionRenderDesc const &distortion, StereoEyeParams const &eyeParams, Vector2f const &pixel ); Vector2f TransformScreenPixelToRendertargetTexUV( Recti const &distortionViewport, DistortionRenderDesc const &distortion, StereoEyeParams const &eyeParams, Vector2f const &pixel ); // A set of "reverse-mapping" functions, mapping from real-world and/or texture space back to the framebuffer. // Be aware that many of these are significantly slower than their forward-mapping counterparts. Vector2f TransformTanFovSpaceToScreenNDC( DistortionRenderDesc const &distortion, const Vector2f &tanEyeAngle, bool usePolyApprox = false ); Vector2f TransformRendertargetNDCToTanFovSpace( const ScaleAndOffset2D &eyeToSourceNDC, const Vector2f &textureNDC ); } //namespace OVR #endif // OVR_Stereo_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_ThreadCommandQueue.cpp b/modules/oculus_sdk_mac/LibOVR/Src/OVR_ThreadCommandQueue.cpp new file mode 100644 index 0000000..7bee2bf --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_ThreadCommandQueue.cpp @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_ThreadCommandQueue.cpp Content : Command queue for operations executed on a thread Created : October 29, 2012 Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "OVR_ThreadCommandQueue.h" namespace OVR { //------------------------------------------------------------------------ // ***** CircularBuffer // CircularBuffer is a FIFO buffer implemented in a single block of memory, // which allows writing and reading variable-size data chucks. Write fails // if buffer is full. class CircularBuffer { enum { AlignSize = 16, AlignMask = AlignSize - 1 }; UByte* pBuffer; UPInt Size; UPInt Tail; // Byte offset of next item to be popped. UPInt Head; // Byte offset of where next push will take place. UPInt End; // When Head < Tail, this is used instead of Size. inline UPInt roundUpSize(UPInt size) { return (size + AlignMask) & ~(UPInt)AlignMask; } public: CircularBuffer(UPInt size) : Size(size), Tail(0), Head(0), End(0) { pBuffer = (UByte*)OVR_ALLOC_ALIGNED(roundUpSize(size), AlignSize); } ~CircularBuffer() { // For ThreadCommands, we must consume everything before shutdown. OVR_ASSERT(IsEmpty()); OVR_FREE_ALIGNED(pBuffer); } bool IsEmpty() const { return (Head == Tail); } // Allocates a state block of specified size and advances pointers, // returning 0 if buffer is full. UByte* Write(UPInt size); // Returns a pointer to next available data block; 0 if none available. UByte* ReadBegin() { return (Head != Tail) ? (pBuffer + Tail) : 0; } // Consumes data of specified size; this must match size passed to Write. void ReadEnd(UPInt size); }; // Allocates a state block of specified size and advances pointers, // returning 0 if buffer is full. UByte* CircularBuffer::Write(UPInt size) { UByte* p = 0; size = roundUpSize(size); // Since this is circular buffer, always allow at least one item. OVR_ASSERT(size < Size/2); if (Head >= Tail) { OVR_ASSERT(End == 0); if (size <= (Size - Head)) { p = pBuffer + Head; Head += size; } else if (size < Tail) { p = pBuffer; End = Head; Head = size; OVR_ASSERT(Head != Tail); } } else { OVR_ASSERT(End != 0); if ((Tail - Head) > size) { p = pBuffer + Head; Head += size; OVR_ASSERT(Head != Tail); } } return p; } void CircularBuffer::ReadEnd(UPInt size) { OVR_ASSERT(Head != Tail); size = roundUpSize(size); Tail += size; if (Tail == End) { Tail = End = 0; } else if (Tail == Head) { OVR_ASSERT(End == 0); Tail = Head = 0; } } //------------------------------------------------------------------------------------- // ***** ThreadCommand ThreadCommand::PopBuffer::~PopBuffer() { if (Size) Destruct(toCommand()); } void ThreadCommand::PopBuffer::InitFromBuffer(void* data) { ThreadCommand* cmd = (ThreadCommand*)data; OVR_ASSERT(cmd->Size <= MaxSize); if (Size) Destruct(toCommand()); Size = cmd->Size; memcpy(Buffer, (void*)cmd, Size); } void ThreadCommand::PopBuffer::Execute() { ThreadCommand* command = toCommand(); OVR_ASSERT(command); command->Execute(); if (NeedsWait()) GetEvent()->PulseEvent(); } //------------------------------------------------------------------------------------- class ThreadCommandQueueImpl : public NewOverrideBase { typedef ThreadCommand::NotifyEvent NotifyEvent; friend class ThreadCommandQueue; public: ThreadCommandQueueImpl(ThreadCommandQueue* queue) : pQueue(queue), ExitEnqueued(false), ExitProcessed(false), CommandBuffer(2048) { } ~ThreadCommandQueueImpl(); bool PushCommand(const ThreadCommand& command); bool PopCommand(ThreadCommand::PopBuffer* popBuffer); // ExitCommand is used by notify us that Thread is shutting down. struct ExitCommand : public ThreadCommand { ThreadCommandQueueImpl* pImpl; ExitCommand(ThreadCommandQueueImpl* impl, bool wait) : ThreadCommand(sizeof(ExitCommand), wait, true), pImpl(impl) { } virtual void Execute() const { Lock::Locker lock(&pImpl->QueueLock); pImpl->ExitProcessed = true; } virtual ThreadCommand* CopyConstruct(void* p) const { return Construct(p, *this); } }; NotifyEvent* AllocNotifyEvent_NTS() { NotifyEvent* p = AvailableEvents.GetFirst(); if (!AvailableEvents.IsNull(p)) p->RemoveNode(); else p = new NotifyEvent; return p; } void FreeNotifyEvent_NTS(NotifyEvent* p) { AvailableEvents.PushBack(p); } void FreeNotifyEvents_NTS() { while(!AvailableEvents.IsEmpty()) { NotifyEvent* p = AvailableEvents.GetFirst(); p->RemoveNode(); delete p; } } ThreadCommandQueue* pQueue; Lock QueueLock; volatile bool ExitEnqueued; volatile bool ExitProcessed; List AvailableEvents; List BlockedProducers; CircularBuffer CommandBuffer; }; ThreadCommandQueueImpl::~ThreadCommandQueueImpl() { Lock::Locker lock(&QueueLock); OVR_ASSERT(BlockedProducers.IsEmpty()); FreeNotifyEvents_NTS(); } bool ThreadCommandQueueImpl::PushCommand(const ThreadCommand& command) { ThreadCommand::NotifyEvent* completeEvent = 0; ThreadCommand::NotifyEvent* queueAvailableEvent = 0; // Repeat writing command into buffer until it is available. do { { // Lock Scope Lock::Locker lock(&QueueLock); if (queueAvailableEvent) { FreeNotifyEvent_NTS(queueAvailableEvent); queueAvailableEvent = 0; } // Don't allow any commands after PushExitCommand() is called. if (ExitEnqueued && !command.ExitFlag) return false; bool bufferWasEmpty = CommandBuffer.IsEmpty(); UByte* buffer = CommandBuffer.Write(command.GetSize()); if (buffer) { ThreadCommand* c = command.CopyConstruct(buffer); if (c->NeedsWait()) completeEvent = c->pEvent = AllocNotifyEvent_NTS(); // Signal-waker consumer when we add data to buffer. if (bufferWasEmpty) pQueue->OnPushNonEmpty_Locked(); break; } queueAvailableEvent = AllocNotifyEvent_NTS(); BlockedProducers.PushBack(queueAvailableEvent); } // Lock Scope queueAvailableEvent->Wait(); } while(1); // Command was enqueued, wait if necessary. if (completeEvent) { completeEvent->Wait(); Lock::Locker lock(&QueueLock); FreeNotifyEvent_NTS(completeEvent); } return true; } // Pops the next command from the thread queue, if any is available. bool ThreadCommandQueueImpl::PopCommand(ThreadCommand::PopBuffer* popBuffer) { Lock::Locker lock(&QueueLock); UByte* buffer = CommandBuffer.ReadBegin(); if (!buffer) { // Notify thread while in lock scope, enabling initialization of wait. pQueue->OnPopEmpty_Locked(); return false; } popBuffer->InitFromBuffer(buffer); CommandBuffer.ReadEnd(popBuffer->GetSize()); if (!BlockedProducers.IsEmpty()) { ThreadCommand::NotifyEvent* queueAvailableEvent = BlockedProducers.GetFirst(); queueAvailableEvent->RemoveNode(); queueAvailableEvent->PulseEvent(); // Event is freed later by waiter. } return true; } //------------------------------------------------------------------------------------- ThreadCommandQueue::ThreadCommandQueue() { pImpl = new ThreadCommandQueueImpl(this); } ThreadCommandQueue::~ThreadCommandQueue() { delete pImpl; } bool ThreadCommandQueue::PushCommand(const ThreadCommand& command) { return pImpl->PushCommand(command); } bool ThreadCommandQueue::PopCommand(ThreadCommand::PopBuffer* popBuffer) { return pImpl->PopCommand(popBuffer); } void ThreadCommandQueue::PushExitCommand(bool wait) { // Exit is processed in two stages: // - First, ExitEnqueued flag is set to block further commands from queuing up. // - Second, the actual exit call is processed on the consumer thread, flushing // any prior commands. // IsExiting() only returns true after exit has flushed. { Lock::Locker lock(&pImpl->QueueLock); if (pImpl->ExitEnqueued) return; pImpl->ExitEnqueued = true; } PushCommand(ThreadCommandQueueImpl::ExitCommand(pImpl, wait)); } bool ThreadCommandQueue::IsExiting() const { return pImpl->ExitProcessed; } } // namespace OVR \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/OVR_ThreadCommandQueue.h b/modules/oculus_sdk_mac/LibOVR/Src/OVR_ThreadCommandQueue.h new file mode 100644 index 0000000..e2d6e71 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/OVR_ThreadCommandQueue.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: None Filename : OVR_ThreadCommandQueue.h Content : Command queue for operations executed on a thread Created : October 29, 2012 Author : Michael Antonov Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. ************************************************************************************/ #ifndef OVR_ThreadCommandQueue_h #define OVR_ThreadCommandQueue_h #include "Kernel/OVR_Types.h" #include "Kernel/OVR_List.h" #include "Kernel/OVR_Atomic.h" #include "Kernel/OVR_Threads.h" namespace OVR { class ThreadCommand; class ThreadCommandQueue; //------------------------------------------------------------------------------------- // ***** ThreadCommand // ThreadCommand is a base class implementation for commands stored in ThreadCommandQueue. class ThreadCommand { public: // NotifyEvent is used by ThreadCommandQueue::PushCallAndWait to notify the // calling (producer) thread when command is completed or queue slot is available. class NotifyEvent : public ListNode, public NewOverrideBase { Event E; public: NotifyEvent() { } void Wait() { E.Wait(); } void PulseEvent() { E.PulseEvent(); } }; // ThreadCommand::PopBuffer is temporary storage for a command popped off // by ThreadCommandQueue::PopCommand. class PopBuffer { enum { MaxSize = 256 }; UPInt Size; union { UByte Buffer[MaxSize]; UPInt Align; }; ThreadCommand* toCommand() const { return (ThreadCommand*)Buffer; } public: PopBuffer() : Size(0) { } ~PopBuffer(); void InitFromBuffer(void* data); bool HasCommand() const { return Size != 0; } UPInt GetSize() const { return Size; } bool NeedsWait() const { return toCommand()->NeedsWait(); } NotifyEvent* GetEvent() const { return toCommand()->pEvent; } // Execute the command and also notifies caller to finish waiting, // if necessary. void Execute(); }; UInt16 Size; bool WaitFlag; bool ExitFlag; // Marks the last exit command. NotifyEvent* pEvent; ThreadCommand(UPInt size, bool waitFlag, bool exitFlag = false) : Size((UInt16)size), WaitFlag(waitFlag), ExitFlag(exitFlag), pEvent(0) { } virtual ~ThreadCommand() { } bool NeedsWait() const { return WaitFlag; } UPInt GetSize() const { return Size; } virtual void Execute() const = 0; // Copy constructor used for serializing this to memory buffer. virtual ThreadCommand* CopyConstruct(void* p) const = 0; }; //------------------------------------------------------------------------------------- // CleanType is a template that strips 'const' and '&' modifiers from the argument type; // for example, typename CleanType::Type is equivalent to A. template struct CleanType { typedef T Type; }; template struct CleanType { typedef T Type; }; template struct CleanType { typedef T Type; }; template struct CleanType { typedef T Type; }; // SelfType is a template that yields the argument type. This helps avoid conflicts with // automatic template argument deduction for function calls when identical argument // is already defined. template struct SelfType { typedef T Type; }; //------------------------------------------------------------------------------------- // ThreadCommand specializations for member functions with different number of // arguments and argument types. // Used to return nothing from a ThreadCommand, to avoid problems with 'void'. struct Void { Void() {} Void(int) {} }; // ThreadCommand for member function with 0 arguments. template class ThreadCommandMF0 : public ThreadCommand { typedef R (C::*FnPtr)(); C* pClass; FnPtr pFn; R* pRet; void executeImpl() const { pRet ? (void)(*pRet = (pClass->*pFn)()) : (void)(pClass->*pFn)(); } public: ThreadCommandMF0(C* pclass, FnPtr fn, R* ret, bool needsWait) : ThreadCommand(sizeof(ThreadCommandMF0), needsWait), pClass(pclass), pFn(fn), pRet(ret) { } virtual void Execute() const { executeImpl(); } virtual ThreadCommand* CopyConstruct(void* p) const { return Construct(p, *this); } }; // ThreadCommand for member function with 1 argument. template class ThreadCommandMF1 : public ThreadCommand { typedef R (C::*FnPtr)(A0); C* pClass; FnPtr pFn; R* pRet; typename CleanType::Type AVal0; void executeImpl() const { pRet ? (void)(*pRet = (pClass->*pFn)(AVal0)) : (void)(pClass->*pFn)(AVal0); } public: ThreadCommandMF1(C* pclass, FnPtr fn, R* ret, A0 a0, bool needsWait) : ThreadCommand(sizeof(ThreadCommandMF1), needsWait), pClass(pclass), pFn(fn), pRet(ret), AVal0(a0) { } virtual void Execute() const { executeImpl(); } virtual ThreadCommand* CopyConstruct(void* p) const { return Construct(p, *this); } }; // ThreadCommand for member function with 2 arguments. template class ThreadCommandMF2 : public ThreadCommand { typedef R (C::*FnPtr)(A0, A1); C* pClass; FnPtr pFn; R* pRet; typename CleanType::Type AVal0; typename CleanType::Type AVal1; void executeImpl() const { pRet ? (void)(*pRet = (pClass->*pFn)(AVal0, AVal1)) : (void)(pClass->*pFn)(AVal0, AVal1); } public: ThreadCommandMF2(C* pclass, FnPtr fn, R* ret, A0 a0, A1 a1, bool needsWait) : ThreadCommand(sizeof(ThreadCommandMF2), needsWait), pClass(pclass), pFn(fn), pRet(ret), AVal0(a0), AVal1(a1) { } virtual void Execute() const { executeImpl(); } virtual ThreadCommand* CopyConstruct(void* p) const { return Construct(p, *this); } }; //------------------------------------------------------------------------------------- // ***** ThreadCommandQueue // ThreadCommandQueue is a queue of executable function-call commands intended to be // serviced by a single consumer thread. Commands are added to the queue with PushCall // and removed with PopCall; they are processed in FIFO order. Multiple producer threads // are supported and will be blocked if internal data buffer is full. class ThreadCommandQueue { public: ThreadCommandQueue(); virtual ~ThreadCommandQueue(); // Pops the next command from the thread queue, if any is available. // The command should be executed by calling popBuffer->Execute(). // Returns 'false' if no command is available at the time of the call. bool PopCommand(ThreadCommand::PopBuffer* popBuffer); // Generic implementaion of PushCommand; enqueues a command for execution. // Returns 'false' if push failed, usually indicating thread shutdown. bool PushCommand(const ThreadCommand& command); // void PushExitCommand(bool wait); // Returns 'true' once ExitCommand has been processed, so the thread can shut down. bool IsExiting() const; // These two virtual functions serve as notifications for derived // thread waiting. virtual void OnPushNonEmpty_Locked() { } virtual void OnPopEmpty_Locked() { } // *** PushCall with no result // Enqueue a member function of 'this' class to be called on consumer thread. // By default the function returns immediately; set 'wait' argument to 'true' to // wait for completion. template bool PushCall(R (C::*fn)(), bool wait = false) { return PushCommand(ThreadCommandMF0(static_cast(this), fn, 0, wait)); } template bool PushCall(R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) { return PushCommand(ThreadCommandMF1(static_cast(this), fn, 0, a0, wait)); } template bool PushCall(R (C::*fn)(A0, A1), typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) { return PushCommand(ThreadCommandMF2(static_cast(this), fn, 0, a0, a1, wait)); } // Enqueue a specified member function call of class C. // By default the function returns immediately; set 'wait' argument to 'true' to // wait for completion. template bool PushCall(C* p, R (C::*fn)(), bool wait = false) { return PushCommand(ThreadCommandMF0(p, fn, 0, wait)); } template bool PushCall(C* p, R (C::*fn)(A0), typename SelfType::Type a0, bool wait = false) { return PushCommand(ThreadCommandMF1(p, fn, 0, a0, wait)); } template bool PushCall(C* p, R (C::*fn)(A0, A1), typename SelfType::Type a0, typename SelfType::Type a1, bool wait = false) { return PushCommand(ThreadCommandMF2(p, fn, 0, a0, a1, wait)); } // *** PushCall with Result // Enqueue a member function of 'this' class call and wait for call to complete // on consumer thread before returning. template bool PushCallAndWaitResult(R (C::*fn)(), R* ret) { return PushCommand(ThreadCommandMF0(static_cast(this), fn, ret, true)); } template bool PushCallAndWaitResult(R (C::*fn)(A0), R* ret, typename SelfType::Type a0) { return PushCommand(ThreadCommandMF1(static_cast(this), fn, ret, a0, true)); } template bool PushCallAndWaitResult(R (C::*fn)(A0, A1), R* ret, typename SelfType::Type a0, typename SelfType::Type a1) { return PushCommand(ThreadCommandMF2(static_cast(this), fn, ret, a0, a1, true)); } // Enqueue a member function call for class C and wait for the call to complete // on consumer thread before returning. template bool PushCallAndWaitResult(C* p, R (C::*fn)(), R* ret) { return PushCommand(ThreadCommandMF0(p, fn, ret, true)); } template bool PushCallAndWaitResult(C* p, R (C::*fn)(A0), R* ret, typename SelfType::Type a0) { return PushCommand(ThreadCommandMF1(p, fn, ret, a0, true)); } template bool PushCallAndWaitResult(C* p, R (C::*fn)(A0, A1), R* ret, typename SelfType::Type a0, typename SelfType::Type a1) { return PushCommand(ThreadCommandMF2(p, fn, ret, a0, a1, true)); } private: class ThreadCommandQueueImpl* pImpl; }; } #endif // OVR_ThreadCommandQueue_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_ImageWindow.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_ImageWindow.cpp new file mode 100644 index 0000000..ef86414 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_ImageWindow.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Util_ImageWindow.cpp Content : An output object for windows that can display raw images for testing Created : March 13, 2014 Authors : Dean Beeler Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "../../Include/OVR.h" #include "Util_ImageWindow.h" #if defined(OVR_OS_WIN32) #include #include "DWrite.h" typedef HRESULT (WINAPI *D2D1CreateFactoryFn)( _In_ D2D1_FACTORY_TYPE, _In_ REFIID, _In_opt_ const D2D1_FACTORY_OPTIONS*, _Out_ ID2D1Factory ** ); typedef HRESULT (WINAPI *DWriteCreateFactoryFn)( _In_ DWRITE_FACTORY_TYPE factoryType, _In_ REFIID iid, _Out_ IUnknown **factory ); namespace OVR { namespace Util { ID2D1Factory* ImageWindow::pD2DFactory = NULL; IDWriteFactory* ImageWindow::pDWriteFactory = NULL; ImageWindow* ImageWindow::globalWindow[4]; int ImageWindow::windowCount = 0; LRESULT CALLBACK MainWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: return 0; case WM_PAINT: { LONG_PTR ptr = GetWindowLongPtr( hwnd, GWLP_USERDATA ); if( ptr ) { ImageWindow* iw = (ImageWindow*)ptr; iw->OnPaint(); } } return 0; case WM_SIZE: // Set the size and position of the window. return 0; case WM_DESTROY: // Clean up window-specific data objects. return 0; // // Process other messages. // default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } //return 0; } ImageWindow::ImageWindow( uint32_t width, uint32_t height ) : frontBufferMutex( new Mutex() ) { HINSTANCE hInst = LoadLibrary( L"d2d1.dll" ); HINSTANCE hInstWrite = LoadLibrary( L"Dwrite.dll" ); D2D1CreateFactoryFn createFactory = NULL; DWriteCreateFactoryFn writeFactory = NULL; if( hInst ) { createFactory = (D2D1CreateFactoryFn)GetProcAddress( hInst, "D2D1CreateFactory" ); } if( hInstWrite ) { writeFactory = (DWriteCreateFactoryFn)GetProcAddress( hInstWrite, "DWriteCreateFactory" ); } globalWindow[windowCount] = this; ++windowCount; if( pD2DFactory == NULL && createFactory && writeFactory ) { createFactory( D2D1_FACTORY_TYPE_MULTI_THREADED, __uuidof(ID2D1Factory), NULL, &pD2DFactory ); // Create a DirectWrite factory. writeFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(pDWriteFactory), reinterpret_cast(&pDWriteFactory) ); } resolution = D2D1::SizeU( width, height ); SetWindowLongPtr( hWindow, GWLP_USERDATA, (LONG_PTR)this ); pRT = NULL; greyBitmap = NULL; colorBitmap = NULL; } ImageWindow::~ImageWindow() { for( int i = 0; i < MaxWindows; ++i ) { if( globalWindow[i] == this ) { globalWindow[i] = NULL; break; } } if( greyBitmap ) greyBitmap->Release(); if( colorBitmap ) colorBitmap->Release(); if( pRT ) pRT->Release(); { Mutex::Locker locker( frontBufferMutex ); while( frames.GetSize() ) { Ptr aFrame = frames.PopBack(); } } delete frontBufferMutex; ShowWindow( hWindow, SW_HIDE ); DestroyWindow( hWindow ); } void ImageWindow::AssociateSurface( void* surface ) { // Assume an IUnknown IUnknown* unknown = (IUnknown*)surface; IDXGISurface *pDxgiSurface = NULL; HRESULT hr = unknown->QueryInterface(&pDxgiSurface); if( hr == S_OK ) { D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), 96, 96 ); pRT = NULL; ID2D1RenderTarget* tmpTarget; hr = pD2DFactory->CreateDxgiSurfaceRenderTarget( pDxgiSurface, &props, &tmpTarget ); if( hr == S_OK ) { DXGI_SURFACE_DESC desc = {0}; pDxgiSurface->GetDesc( &desc ); int width = desc.Width; int height = desc.Height; D2D1_SIZE_U size = D2D1::SizeU( width, height ); D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat( DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED ); D2D1_PIXEL_FORMAT colorPixelFormat = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED ); D2D1_BITMAP_PROPERTIES bitmapProps; bitmapProps.dpiX = 96; bitmapProps.dpiY = 96; bitmapProps.pixelFormat = pixelFormat; D2D1_BITMAP_PROPERTIES colorBitmapProps; colorBitmapProps.dpiX = 96; colorBitmapProps.dpiY = 96; colorBitmapProps.pixelFormat = colorPixelFormat; HRESULT result = tmpTarget->CreateBitmap( size, bitmapProps, &greyBitmap ); if( result != S_OK ) { tmpTarget->Release(); tmpTarget = NULL; } result = tmpTarget->CreateBitmap( size, colorBitmapProps, &colorBitmap ); if( result != S_OK ) { greyBitmap->Release(); greyBitmap = NULL; tmpTarget->Release(); tmpTarget = NULL; } pRT = tmpTarget; } } } void ImageWindow::Process() { if( pRT && greyBitmap ) { OnPaint(); pRT->Flush(); } } void ImageWindow::Complete() { Mutex::Locker locker( frontBufferMutex ); if( frames.IsEmpty() ) return; if( frames.PeekBack(0)->ready ) return; Ptr frame = frames.PeekBack(0); frame->ready = true; } void ImageWindow::OnPaint() { Mutex::Locker locker( frontBufferMutex ); // Nothing to do if( frames.IsEmpty() ) return; if( !frames.PeekFront(0)->ready ) return; Ptr currentFrame = frames.PopFront(); Ptr nextFrame = NULL; if( !frames.IsEmpty() ) nextFrame = frames.PeekFront(0); while( nextFrame && nextFrame->ready ) { // Free up the current frame since it's been removed from the deque currentFrame = frames.PopFront(); if( frames.IsEmpty() ) break; nextFrame = frames.PeekFront(0); } if( currentFrame->imageData ) greyBitmap->CopyFromMemory( NULL, currentFrame->imageData, currentFrame->width ); if( currentFrame->colorImageData ) colorBitmap->CopyFromMemory( NULL, currentFrame->colorImageData, currentFrame->colorPitch ); pRT->BeginDraw(); pRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); pRT->Clear( D2D1::ColorF(D2D1::ColorF::Black) ); // This will mirror our image D2D1_MATRIX_3X2_F m; m._11 = -1; m._12 = 0; m._21 = 0; m._22 = 1; m._31 = 0; m._32 = 0; pRT->SetTransform( m ); ID2D1SolidColorBrush* whiteBrush; pRT->CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::White, 1.0f), &whiteBrush ); if( currentFrame->imageData ) { pRT->FillOpacityMask( greyBitmap, whiteBrush, D2D1_OPACITY_MASK_CONTENT_TEXT_NATURAL, D2D1::RectF( -(FLOAT)resolution.width, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ), //D2D1::RectF( 0.0f, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ), D2D1::RectF( 0.0f, 0.0f, (FLOAT)resolution.width, (FLOAT)resolution.height ) ); } else if( currentFrame->colorImageData ) { pRT->DrawBitmap( colorBitmap, D2D1::RectF( -(FLOAT)resolution.width, 0.0f, (FLOAT)0.0f, (FLOAT)resolution.height ) ); } pRT->SetTransform(D2D1::Matrix3x2F::Identity()); whiteBrush->Release(); Array::Iterator it; for( it = currentFrame->plots.Begin(); it != currentFrame->plots.End(); ++it ) { ID2D1SolidColorBrush* aBrush; pRT->CreateSolidColorBrush( D2D1::ColorF( it->r, it->g, it->b), &aBrush ); D2D1_ELLIPSE ellipse; ellipse.point.x = it->x; ellipse.point.y = it->y; ellipse.radiusX = it->radius; ellipse.radiusY = it->radius; if( it->fill ) pRT->FillEllipse( &ellipse, aBrush ); else pRT->DrawEllipse( &ellipse, aBrush ); aBrush->Release(); } static const WCHAR msc_fontName[] = L"Verdana"; static const FLOAT msc_fontSize = 20; IDWriteTextFormat* textFormat = NULL; // Create a DirectWrite text format object. pDWriteFactory->CreateTextFormat( msc_fontName, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, msc_fontSize, L"", //locale &textFormat ); D2D1_SIZE_F renderTargetSize = pRT->GetSize(); Array::Iterator textIt; for( textIt = currentFrame->textLines.Begin(); textIt != currentFrame->textLines.End(); ++textIt ) { ID2D1SolidColorBrush* aBrush; pRT->CreateSolidColorBrush( D2D1::ColorF( textIt->r, textIt->g, textIt->b), &aBrush ); WCHAR* tmpString = (WCHAR*)calloc( textIt->text.GetLength(), sizeof( WCHAR ) ); for( unsigned i = 0; i < textIt->text.GetLength(); ++i ) { tmpString[i] = (WCHAR)textIt->text.GetCharAt( i ); } pRT->DrawTextW( tmpString, (UINT32)textIt->text.GetLength(), textFormat, D2D1::RectF(textIt->x, textIt->y, renderTargetSize.width, renderTargetSize.height), aBrush ); free( tmpString ); aBrush->Release(); } if( textFormat ) textFormat->Release(); pRT->EndDraw(); pRT->Flush(); } Ptr ImageWindow::lastUnreadyFrame() { static int framenumber = 0; if( frames.GetSize() && !frames.PeekBack( 0 )->ready ) return frames.PeekBack( 0 ); // Create a new frame if an unready one doesn't already exist Ptr tmpFrame = *new Frame( framenumber ); frames.PushBack( tmpFrame ); ++framenumber; return tmpFrame; } void ImageWindow::UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height ) { if( pRT && greyBitmap ) { Mutex::Locker locker( frontBufferMutex ); Ptr frame = lastUnreadyFrame(); frame->imageData = malloc( width * height ); frame->width = width; frame->height = height; memcpy( frame->imageData, imageData, width * height ); } } void ImageWindow::UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch ) { if( pRT && colorBitmap ) { Mutex::Locker locker( frontBufferMutex ); Ptr frame = lastUnreadyFrame(); frame->colorImageData = malloc( pitch * height ); frame->width = width; frame->height = height; frame->colorPitch = pitch; memcpy( frame->colorImageData, imageData, pitch * height ); } } void ImageWindow::addCircle( float x, float y, float radius, float r, float g, float b, bool fill ) { if( pRT ) { CirclePlot cp; cp.x = x; cp.y = y; cp.radius = radius; cp.r = r; cp.g = g; cp.b = b; cp.fill = fill; Mutex::Locker locker( frontBufferMutex ); Ptr frame = lastUnreadyFrame(); frame->plots.PushBack( cp ); } } void ImageWindow::addText( float x, float y, float r, float g, float b, OVR::String text ) { if( pRT ) { TextPlot tp; tp.x = x; tp.y = y; tp.r = r; tp.g = g; tp.b = b; tp.text = text; Mutex::Locker locker( frontBufferMutex ); Ptr frame = lastUnreadyFrame(); frame->textLines.PushBack( tp ); } } }} #endif //defined(OVR_OS_WIN32) \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_ImageWindow.h b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_ImageWindow.h new file mode 100644 index 0000000..1ac0f5b --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_ImageWindow.h @@ -0,0 +1 @@ +/************************************************************************************ Filename : Util_ImageWindow.h Content : An output object for windows that can display raw images for testing Created : March 13, 2014 Authors : Dean Beeler Copyright : Copyright 2014 Oculus, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef UTIL_IMAGEWINDOW_H #define UTIL_IMAGEWINDOW_H #if defined(OVR_OS_WIN32) #define WIN32_LEAN_AND_MEAN 1 #include #include #include #endif #include "../../Include/OVR.h" #include "../Kernel/OVR_Hash.h" #include "../Kernel/OVR_Array.h" #include "../Kernel/OVR_Threads.h" #include "../Kernel/OVR_Deque.h" #include namespace OVR { namespace Util { typedef struct { float x; float y; float radius; float r; float g; float b; bool fill; } CirclePlot; typedef struct { float x; float y; float r; float g; float b; OVR::String text; } TextPlot; class Frame : virtual public RefCountBaseV { public: Frame( int frame ) : frameNumber( frame ), imageData( NULL ), colorImageData( NULL ), plots(), textLines(), width( 0 ), height( 0 ), colorPitch( 0 ), ready( false ) { } ~Frame() { if( imageData ) free( imageData ); if( colorImageData ) free( colorImageData ); plots.ClearAndRelease(); textLines.ClearAndRelease(); } int frameNumber; Array plots; Array textLines; void* imageData; void* colorImageData; int width; int height; int colorPitch; bool ready; }; #if defined(OVR_OS_WIN32) class ImageWindow { HWND hWindow; ID2D1RenderTarget* pRT; D2D1_SIZE_U resolution; Mutex* frontBufferMutex; InPlaceMutableDeque< Ptr > frames; ID2D1Bitmap* greyBitmap; ID2D1Bitmap* colorBitmap; public: // constructors ImageWindow(); ImageWindow( uint32_t width, uint32_t height ); virtual ~ImageWindow(); void GetResolution( size_t& width, size_t& height ) { width = resolution.width; height = resolution.height; } void OnPaint(); // Called by Windows when it receives a WM_PAINT message void UpdateImage( const uint8_t* imageData, uint32_t width, uint32_t height ) { UpdateImageBW( imageData, width, height ); } void UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height ); void UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch ); void Complete(); // Called by drawing thread to submit a frame void Process(); // Called by rendering thread to do window processing void AssociateSurface( void* surface ); void addCircle( float x , float y, float radius, float r, float g, float b, bool fill ); void addText( float x, float y, float r, float g, float b, OVR::String text ); static ImageWindow* GlobalWindow( int window ) { return globalWindow[window]; } static int WindowCount() { return windowCount; } private: Ptr lastUnreadyFrame(); static const int MaxWindows = 4; static ImageWindow* globalWindow[MaxWindows]; static int windowCount; static ID2D1Factory* pD2DFactory; static IDWriteFactory* pDWriteFactory; }; #else class ImageWindow { public: // constructors ImageWindow() {} ImageWindow( uint32_t width, uint32_t height ) { OVR_UNUSED( width ); OVR_UNUSED( height ); } virtual ~ImageWindow() { } void GetResolution( size_t& width, size_t& height ) { width = 0; height = 0; } void OnPaint() { } void UpdateImage( const uint8_t* imageData, uint32_t width, uint32_t height ) { UpdateImageBW( imageData, width, height ); } void UpdateImageBW( const uint8_t* imageData, uint32_t width, uint32_t height ) { } void UpdateImageRGBA( const uint8_t* imageData, uint32_t width, uint32_t height, uint32_t pitch ) { } void Complete() { } void Process() { } void AssociateSurface( void* surface ) { } void addCircle( float x , float y, float radius, float r, float g, float b, bool fill ) { } void addText( float x, float y, float r, float g, float b, OVR::String text ) { } static ImageWindow* GlobalWindow( int window ) { return globalWindow[window]; } static int WindowCount() { return windowCount; } private: static const int MaxWindows = 4; static ImageWindow* globalWindow[4]; static int windowCount; }; #endif }} // namespace OVR::Util #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Interface.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Interface.cpp new file mode 100644 index 0000000..c02ec2a --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Interface.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Util_Interface.cpp Content : Simple interface, utilised by internal demos, with access to wider SDK as needed. Located in the body of the SDK to ensure updated when new SDK features are added. Created : February 20, 2014 Authors : Tom Heath Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "Util_Interface.h" //Files left in to ease its possible return...... \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Interface.h b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Interface.h new file mode 100644 index 0000000..527944c --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Interface.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_Interface.h Content : Simple interface, utilised by internal demos, with access to wider SDK as needed. Located in the body of the SDK to ensure updated when new SDK features are added. Created : February 20, 2014 Authors : Tom Heath Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Util_Interface_h #define OVR_Util_Interface_h #include "../../Src/OVR_CAPI.h" //Files left in to ease its possible return...... #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest.cpp new file mode 100644 index 0000000..056e0e8 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Util_LatencyTest.cpp Content : Wraps the lower level LatencyTester interface and adds functionality. Created : February 14, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "Util_LatencyTest.h" #include "../Kernel/OVR_Log.h" #include "../Kernel/OVR_Timer.h" namespace OVR { namespace Util { static const UInt32 TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION = 16*10; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION = 16*10; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT = 16*5; static const UInt32 TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS = 16*5; static const UInt32 DEFAULT_NUMBER_OF_SAMPLES = 10; // For both color 1->2 and color 2->1 transitions. static const UInt32 INITIAL_SAMPLES_TO_IGNORE = 4; static const UInt32 TIMEOUT_WAITING_FOR_TEST_STARTED = 1000; static const UInt32 TIMEOUT_WAITING_FOR_COLOR_DETECTED = 4000; static const Color CALIBRATE_BLACK(0, 0, 0); static const Color CALIBRATE_WHITE(255, 255, 255); static const Color COLOR1(0, 0, 0); static const Color COLOR2(255, 255, 255); static const Color SENSOR_DETECT_THRESHOLD(128, 255, 255); static const float BIG_FLOAT = 1000000.0f; static const float SMALL_FLOAT = -1000000.0f; //------------------------------------------------------------------------------------- // ***** LatencyTest LatencyTest::LatencyTest(LatencyTestDevice* device) : Handler(getThis()) { if (device != NULL) { SetDevice(device); } reset(); srand(Timer::GetTicksMs()); } LatencyTest::~LatencyTest() { clearMeasurementResults(); } bool LatencyTest::SetDevice(LatencyTestDevice* device) { if (device != Device) { Handler.RemoveHandlerFromDevices(); Device = device; if (Device != NULL) { Device->AddMessageHandler(&Handler); // Set trigger threshold. LatencyTestConfiguration configuration(SENSOR_DETECT_THRESHOLD, false); // No samples streaming. Device->SetConfiguration(configuration, true); // Set display to initial (3 dashes). LatencyTestDisplay ltd(2, 0x40400040); Device->SetDisplay(ltd); } } return true; } UInt32 LatencyTest::getRandomComponent(UInt32 range) { UInt32 val = rand() % range; return val; } void LatencyTest::BeginTest() { if (State == State_WaitingForButton) { // Set color to black and wait a while. RenderColor = CALIBRATE_BLACK; State = State_WaitingForSettlePreCalibrationColorBlack; OVR_DEBUG_LOG(("State_WaitingForButton -> State_WaitingForSettlePreCalibrationColorBlack.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); } } void LatencyTest::handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage) { // For debugging. /* if (msg.Type == Message_LatencyTestSamples) { MessageLatencyTestSamples* pSamples = (MessageLatencyTestSamples*) &msg; if (pSamples->Samples.GetSize() > 0) { // Just show the first one for now. Color c = pSamples->Samples[0]; OVR_DEBUG_LOG(("%d %d %d", c.R, c.G, c.B)); } return; } */ if (latencyTestMessage == LatencyTest_Timer) { if (!Device) { reset(); return; } if (State == State_WaitingForSettlePreCalibrationColorBlack) { // Send calibrate message to device and wait a while. Device->SetCalibrate(CALIBRATE_BLACK); State = State_WaitingForSettlePostCalibrationColorBlack; OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorBlack -> State_WaitingForSettlePostCalibrationColorBlack.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); } else if (State == State_WaitingForSettlePostCalibrationColorBlack) { // Change color to white and wait a while. RenderColor = CALIBRATE_WHITE; State = State_WaitingForSettlePreCalibrationColorWhite; OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorBlack -> State_WaitingForSettlePreCalibrationColorWhite.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_PRE_CALIBRATION); } else if (State == State_WaitingForSettlePreCalibrationColorWhite) { // Send calibrate message to device and wait a while. Device->SetCalibrate(CALIBRATE_WHITE); State = State_WaitingForSettlePostCalibrationColorWhite; OVR_DEBUG_LOG(("State_WaitingForSettlePreCalibrationColorWhite -> State_WaitingForSettlePostCalibrationColorWhite.")); setTimer(TIME_TO_WAIT_FOR_SETTLE_POST_CALIBRATION); } else if (State == State_WaitingForSettlePostCalibrationColorWhite) { // Calibration is done. Switch to color 1 and wait for it to settle. RenderColor = COLOR1; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("State_WaitingForSettlePostCalibrationColorWhite -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } else if (State == State_WaitingForSettlePostMeasurement) { // Prepare for next measurement. // Create a new result object. MeasurementResult* pResult = new MeasurementResult(); Results.PushBack(pResult); State = State_WaitingToTakeMeasurement; OVR_DEBUG_LOG(("State_WaitingForSettlePostMeasurement -> State_WaitingToTakeMeasurement.")); } else if (State == State_WaitingForTestStarted) { // We timed out waiting for 'TestStarted'. Abandon this measurement and setup for the next. getActiveResult()->TimedOutWaitingForTestStarted = true; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("** Timed out waiting for 'TestStarted'.")); OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } else if (State == State_WaitingForColorDetected) { // We timed out waiting for 'ColorDetected'. Abandon this measurement and setup for the next. getActiveResult()->TimedOutWaitingForColorDetected = true; State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("** Timed out waiting for 'ColorDetected'.")); OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); } } else if (latencyTestMessage == LatencyTest_ProcessInputs) { if (State == State_WaitingToTakeMeasurement) { if (!Device) { reset(); return; } // Send 'StartTest' feature report with opposite target color. if (RenderColor == COLOR1) { RenderColor = COLOR2; } else { RenderColor = COLOR1; } getActiveResult()->TargetColor = RenderColor; // Record time so we can determine usb roundtrip time. getActiveResult()->StartTestSeconds = Timer::GetSeconds(); Device->SetStartTest(RenderColor); State = State_WaitingForTestStarted; OVR_DEBUG_LOG(("State_WaitingToTakeMeasurement -> State_WaitingForTestStarted.")); setTimer(TIMEOUT_WAITING_FOR_TEST_STARTED); LatencyTestDisplay ltd(2, 0x40090040); Device->SetDisplay(ltd); } } else if (msg.Type == Message_LatencyTestButton) { BeginTest(); } else if (msg.Type == Message_LatencyTestStarted) { if (State == State_WaitingForTestStarted) { clearTimer(); // Record time so we can determine usb roundtrip time. getActiveResult()->TestStartedSeconds = Timer::GetSeconds(); State = State_WaitingForColorDetected; OVR_DEBUG_LOG(("State_WaitingForTestStarted -> State_WaitingForColorDetected.")); setTimer(TIMEOUT_WAITING_FOR_COLOR_DETECTED); } } else if (msg.Type == Message_LatencyTestColorDetected) { if (State == State_WaitingForColorDetected) { // Record time to detect color. MessageLatencyTestColorDetected* pDetected = (MessageLatencyTestColorDetected*) &msg; UInt16 elapsedTime = pDetected->Elapsed; OVR_DEBUG_LOG(("Time to 'ColorDetected' = %d", elapsedTime)); getActiveResult()->DeviceMeasuredElapsedMilliS = elapsedTime; if (areResultsComplete()) { // We're done. processResults(); reset(); } else { // Run another measurement. State = State_WaitingForSettlePostMeasurement; OVR_DEBUG_LOG(("State_WaitingForColorDetected -> State_WaitingForSettlePostMeasurement.")); UInt32 waitTime = TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT + getRandomComponent(TIME_TO_WAIT_FOR_SETTLE_POST_MEASUREMENT_RANDOMNESS); setTimer(waitTime); LatencyTestDisplay ltd(2, 0x40400040); Device->SetDisplay(ltd); } } } else if (msg.Type == Message_DeviceRemoved) { reset(); } } LatencyTest::MeasurementResult* LatencyTest::getActiveResult() { OVR_ASSERT(!Results.IsEmpty()); return Results.GetLast(); } void LatencyTest::setTimer(UInt32 timeMilliS) { ActiveTimerMilliS = timeMilliS; } void LatencyTest::clearTimer() { ActiveTimerMilliS = 0; } void LatencyTest::reset() { clearMeasurementResults(); State = State_WaitingForButton; HaveOldTime = false; ActiveTimerMilliS = 0; } void LatencyTest::clearMeasurementResults() { while(!Results.IsEmpty()) { MeasurementResult* pElem = Results.GetFirst(); pElem->RemoveNode(); delete pElem; } } LatencyTest::LatencyTestHandler::~LatencyTestHandler() { RemoveHandlerFromDevices(); } void LatencyTest::LatencyTestHandler::OnMessage(const Message& msg) { pLatencyTestUtil->handleMessage(msg); } void LatencyTest::ProcessInputs() { updateForTimeouts(); handleMessage(Message(), LatencyTest_ProcessInputs); } bool LatencyTest::DisplayScreenColor(Color& colorToDisplay) { updateForTimeouts(); if (State == State_WaitingForButton) { return false; } colorToDisplay = RenderColor; return true; } const char* LatencyTest::GetResultsString() { if (!ResultsString.IsEmpty() && ReturnedResultString != ResultsString.ToCStr()) { ReturnedResultString = ResultsString; return ReturnedResultString.ToCStr(); } return NULL; } bool LatencyTest::areResultsComplete() { UInt32 initialMeasurements = 0; UInt32 measurements1to2 = 0; UInt32 measurements2to1 = 0; MeasurementResult* pCurr = Results.GetFirst(); while(true) { // Process. if (!pCurr->TimedOutWaitingForTestStarted && !pCurr->TimedOutWaitingForColorDetected) { initialMeasurements++; if (initialMeasurements > INITIAL_SAMPLES_TO_IGNORE) { if (pCurr->TargetColor == COLOR2) { measurements1to2++; } else { measurements2to1++; } } } if (Results.IsLast(pCurr)) { break; } pCurr = Results.GetNext(pCurr); } if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) { return true; } return false; } void LatencyTest::processResults() { UInt32 minTime1To2 = UINT_MAX; UInt32 maxTime1To2 = 0; float averageTime1To2 = 0.0f; UInt32 minTime2To1 = UINT_MAX; UInt32 maxTime2To1 = 0; float averageTime2To1 = 0.0f; float minUSBTripMilliS = BIG_FLOAT; float maxUSBTripMilliS = SMALL_FLOAT; float averageUSBTripMilliS = 0.0f; UInt32 countUSBTripTime = 0; UInt32 measurementsCount = 0; UInt32 measurements1to2 = 0; UInt32 measurements2to1 = 0; MeasurementResult* pCurr = Results.GetFirst(); UInt32 count = 0; while(true) { count++; if (!pCurr->TimedOutWaitingForTestStarted && !pCurr->TimedOutWaitingForColorDetected) { measurementsCount++; if (measurementsCount > INITIAL_SAMPLES_TO_IGNORE) { if (pCurr->TargetColor == COLOR2) { measurements1to2++; if (measurements1to2 <= DEFAULT_NUMBER_OF_SAMPLES) { UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; minTime1To2 = Alg::Min(elapsed, minTime1To2); maxTime1To2 = Alg::Max(elapsed, maxTime1To2); averageTime1To2 += (float) elapsed; } } else { measurements2to1++; if (measurements2to1 <= DEFAULT_NUMBER_OF_SAMPLES) { UInt32 elapsed = pCurr->DeviceMeasuredElapsedMilliS; minTime2To1 = Alg::Min(elapsed, minTime2To1); maxTime2To1 = Alg::Max(elapsed, maxTime2To1); averageTime2To1 += (float) elapsed; } } float usbRountripElapsedMilliS = Timer::MsPerSecond * (float) (pCurr->TestStartedSeconds - pCurr->StartTestSeconds); minUSBTripMilliS = Alg::Min(usbRountripElapsedMilliS, minUSBTripMilliS); maxUSBTripMilliS = Alg::Max(usbRountripElapsedMilliS, maxUSBTripMilliS); averageUSBTripMilliS += usbRountripElapsedMilliS; countUSBTripTime++; } } if (measurements1to2 >= DEFAULT_NUMBER_OF_SAMPLES && measurements2to1 >= DEFAULT_NUMBER_OF_SAMPLES) { break; } if (Results.IsLast(pCurr)) { break; } pCurr = Results.GetNext(pCurr); } averageTime1To2 /= (float) DEFAULT_NUMBER_OF_SAMPLES; averageTime2To1 /= (float) DEFAULT_NUMBER_OF_SAMPLES; averageUSBTripMilliS /= countUSBTripTime; float finalResult = 0.5f * (averageTime1To2 + averageTime2To1); finalResult += averageUSBTripMilliS; ResultsString.Clear(); ResultsString.AppendFormat("RESULT=%.1f (add half Tracker period) [b->w %d|%.1f|%d] [w->b %d|%.1f|%d] [usb rndtrp %.1f|%.1f|%.1f] [cnt %d] [tmouts %d]", finalResult, minTime1To2, averageTime1To2, maxTime1To2, minTime2To1, averageTime2To1, maxTime2To1, minUSBTripMilliS, averageUSBTripMilliS, maxUSBTripMilliS, DEFAULT_NUMBER_OF_SAMPLES*2, count - measurementsCount); // Display result on latency tester display. LatencyTestDisplay ltd(1, (int)finalResult); Device->SetDisplay(ltd); } void LatencyTest::updateForTimeouts() { if (!HaveOldTime) { HaveOldTime = true; OldTime = Timer::GetTicksMs(); return; } UInt32 newTime = Timer::GetTicksMs(); UInt32 elapsedMilliS = newTime - OldTime; if (newTime < OldTime) { elapsedMilliS = OldTime - newTime; elapsedMilliS = UINT_MAX - elapsedMilliS; } OldTime = newTime; elapsedMilliS = Alg::Min(elapsedMilliS, (UInt32) 100); // Clamp at 100mS in case we're not being called very often. if (ActiveTimerMilliS == 0) { return; } if (elapsedMilliS >= ActiveTimerMilliS) { ActiveTimerMilliS = 0; handleMessage(Message(), LatencyTest_Timer); return; } ActiveTimerMilliS -= elapsedMilliS; } }} // namespace OVR::Util \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest.h b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest.h new file mode 100644 index 0000000..9c86a94 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_LatencyTest.h Content : Wraps the lower level LatencyTesterDevice and adds functionality. Created : February 14, 2013 Authors : Lee Cooper Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Util_LatencyTest_h #define OVR_Util_LatencyTest_h #include "../OVR_Device.h" #include "../Kernel/OVR_String.h" #include "../Kernel/OVR_List.h" namespace OVR { namespace Util { //------------------------------------------------------------------------------------- // ***** LatencyTest // // LatencyTest utility class wraps the low level LatencyTestDevice and manages the scheduling // of a latency test. A single test is composed of a series of individual latency measurements // which are used to derive min, max, and an average latency value. // // Developers are required to call the following methods: // SetDevice - Sets the LatencyTestDevice to be used for the tests. // ProcessInputs - This should be called at the same place in the code where the game engine // reads the headset orientation from LibOVR (typically done by calling // 'GetOrientation' on the SensorFusion object). Calling this at the right time // enables us to measure the same latency that occurs for headset orientation // changes. // DisplayScreenColor - The latency tester works by sensing the color of the pixels directly // beneath it. The color of these pixels can be set by drawing a small // quad at the end of the rendering stage. The quad should be small // such that it doesn't significantly impact the rendering of the scene, // but large enough to be 'seen' by the sensor. See the SDK // documentation for more information. // GetResultsString - Call this to get a string containing the most recent results. // If the string has already been gotten then NULL will be returned. // The string pointer will remain valid until the next time this // method is called. // class LatencyTest : public NewOverrideBase { public: LatencyTest(LatencyTestDevice* device = NULL); ~LatencyTest(); // Set the Latency Tester device that we'll use to send commands to and receive // notification messages from. bool SetDevice(LatencyTestDevice* device); // Returns true if this LatencyTestUtil has a Latency Tester device. bool HasDevice() const { return Handler.IsHandlerInstalled(); } void ProcessInputs(); bool DisplayScreenColor(Color& colorToDisplay); const char* GetResultsString(); bool IsMeasuringNow() const { return (State != State_WaitingForButton); } // Begin test. Equivalent to pressing the button on the latency tester. void BeginTest(); private: LatencyTest* getThis() { return this; } enum LatencyTestMessageType { LatencyTest_None, LatencyTest_Timer, LatencyTest_ProcessInputs, }; UInt32 getRandomComponent(UInt32 range); void handleMessage(const Message& msg, LatencyTestMessageType latencyTestMessage = LatencyTest_None); void reset(); void setTimer(UInt32 timeMilliS); void clearTimer(); class LatencyTestHandler : public MessageHandler { LatencyTest* pLatencyTestUtil; public: LatencyTestHandler(LatencyTest* latencyTester) : pLatencyTestUtil(latencyTester) { } ~LatencyTestHandler(); virtual void OnMessage(const Message& msg); }; bool areResultsComplete(); void processResults(); void updateForTimeouts(); Ptr Device; LatencyTestHandler Handler; enum TesterState { State_WaitingForButton, State_WaitingForSettlePreCalibrationColorBlack, State_WaitingForSettlePostCalibrationColorBlack, State_WaitingForSettlePreCalibrationColorWhite, State_WaitingForSettlePostCalibrationColorWhite, State_WaitingToTakeMeasurement, State_WaitingForTestStarted, State_WaitingForColorDetected, State_WaitingForSettlePostMeasurement }; TesterState State; bool HaveOldTime; UInt32 OldTime; UInt32 ActiveTimerMilliS; Color RenderColor; struct MeasurementResult : public ListNode, public NewOverrideBase { MeasurementResult() : DeviceMeasuredElapsedMilliS(0), TimedOutWaitingForTestStarted(false), TimedOutWaitingForColorDetected(false), StartTestSeconds(0.0), TestStartedSeconds(0.0) {} Color TargetColor; UInt32 DeviceMeasuredElapsedMilliS; bool TimedOutWaitingForTestStarted; bool TimedOutWaitingForColorDetected; double StartTestSeconds; double TestStartedSeconds; }; List Results; void clearMeasurementResults(); MeasurementResult* getActiveResult(); StringBuffer ResultsString; String ReturnedResultString; }; }} // namespace OVR::Util #endif // OVR_Util_LatencyTest_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest2.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest2.cpp new file mode 100644 index 0000000..b0669e4 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest2.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Util_LatencyTest2.cpp Content : Wraps the lower level LatencyTester interface for DK2 and adds functionality. Created : March 10, 2014 Authors : Volga Aksoy Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "Util_LatencyTest2.h" #include "../OVR_CAPI.h" #include "../Kernel/OVR_Log.h" #include "../Kernel/OVR_Timer.h" namespace OVR { namespace Util { //------------------------------------------------------------------------------------- // ***** LatencyTest2 LatencyTest2::LatencyTest2(SensorDevice* device) : Handler(getThis()) , TestActive(false) , StartTiming(-1) , LatencyMeasuredInSeconds(-1) , LastPixelReadMsg(NULL) , RenderColorValue(0) , NumMsgsBeforeSettle(0) , NumTestsSuccessful(0) { if (device != NULL) { SetSensorDevice(device); } } LatencyTest2::~LatencyTest2() { HmdDevice = NULL; LatencyTesterDev = NULL; Handler.RemoveHandlerFromDevices(); } bool LatencyTest2::SetSensorDevice(SensorDevice* device) { Lock::Locker devLocker(&TesterLock); // Enable/Disable pixel read from HMD if (device != HmdDevice) { Handler.RemoveHandlerFromDevices(); HmdDevice = device; if (HmdDevice != NULL) { HmdDevice->AddMessageHandler(&Handler); } } return true; } bool LatencyTest2::SetDisplayDevice(LatencyTestDevice* device) { Lock::Locker devLocker(&TesterLock); if (device != LatencyTesterDev) { LatencyTesterDev = device; if (LatencyTesterDev != NULL) { // Set display to initial (3 dashes). LatencyTestDisplay ltd(2, 0x40400040); LatencyTesterDev->SetDisplay(ltd); } } return true; } void LatencyTest2::BeginTest(double startTime) { Lock::Locker devLocker(&TesterLock); if (!TestActive) { TestActive = true; NumMsgsBeforeSettle = 0; // Go to next pixel value //RenderColorValue = (RenderColorValue == 0) ? 255 : 0; RenderColorValue = (RenderColorValue + LT2_ColorIncrement) % 256; RawStartTiming = LastPixelReadMsg.RawSensorTime; if (startTime > 0.0) StartTiming = startTime; else StartTiming = ovr_GetTimeInSeconds(); } } void LatencyTest2::handleMessage(const MessagePixelRead& msg) { Lock::Locker devLocker(&TesterLock); // Hold onto the last message as we will use this when we start a new test LastPixelReadMsg = msg; // If color readback index is valid, store it in the lock-less queue. int readbackIndex = 0; if (FrameTimeRecord::ColorToReadbackIndex(&readbackIndex, msg.PixelReadValue)) { RecentFrameSet.AddValue(readbackIndex, msg.FrameTimeSeconds); LockessRecords.SetState(RecentFrameSet); } NumMsgsBeforeSettle++; if (TestActive) { int pixelValueDiff = RenderColorValue - LastPixelReadMsg.PixelReadValue; int rawTimeDiff = LastPixelReadMsg.RawFrameTime - RawStartTiming; if (pixelValueDiff < LT2_PixelTestThreshold && pixelValueDiff > -LT2_PixelTestThreshold) { TestActive = false; LatencyMeasuredInSeconds = LastPixelReadMsg.FrameTimeSeconds - StartTiming; RawLatencyMeasured = rawTimeDiff; //LatencyMeasuredInSeconds = RawLatencyMeasured / 1000000.0; if(LatencyTesterDev && (NumTestsSuccessful % 5) == 0) { int displayNum = (int)(RawLatencyMeasured / 100.0); //int displayNum = NumMsgsBeforeSettle; //int displayNum = (int)(LatencyMeasuredInSeconds * 1000.0); LatencyTestDisplay ltd(1, displayNum); LatencyTesterDev->SetDisplay(ltd); } NumTestsSuccessful++; } else if (TestActive && (rawTimeDiff / 1000) > LT2_TimeoutWaitingForColorDetected) { TestActive = false; LatencyMeasuredInSeconds = -1; } } } LatencyTest2::PixelReadHandler::~PixelReadHandler() { RemoveHandlerFromDevices(); } void LatencyTest2::PixelReadHandler::OnMessage(const Message& msg) { if(msg.Type == Message_PixelRead) pLatencyTestUtil->handleMessage(static_cast(msg)); } bool LatencyTest2::DisplayScreenColor(Color& colorToDisplay) { Lock::Locker devLocker(&TesterLock); colorToDisplay = Color(RenderColorValue, RenderColorValue, RenderColorValue, 255); return TestActive; } }} // namespace OVR::Util \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest2.h b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest2.h new file mode 100644 index 0000000..dacb502 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_LatencyTest2.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_LatencyTest2.h Content : Wraps the lower level LatencyTester interface for DK2 and adds functionality. Created : March 10, 2014 Authors : Volga Aksoy Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Util_LatencyTest2_h #define OVR_Util_LatencyTest2_h #include "../OVR_Device.h" #include "../Kernel/OVR_String.h" #include "../Kernel/OVR_List.h" #include "../Kernel/OVR_Lockless.h" namespace OVR { namespace Util { enum { LT2_ColorIncrement = 32, LT2_PixelTestThreshold = LT2_ColorIncrement / 3, LT2_IncrementCount = 256 / LT2_ColorIncrement, LT2_TimeoutWaitingForColorDetected = 1000 // 1 second }; //------------------------------------------------------------------------------------- // Describes frame scanout time used for latency testing. struct FrameTimeRecord { int ReadbackIndex; double TimeSeconds; // Utility functions to convert color to readBack indices and back. // The purpose of ReadbackIndex is to allow direct comparison by value. static bool ColorToReadbackIndex(int *readbackIndex, unsigned char color) { int compareColor = color - LT2_ColorIncrement/2; int index = color / LT2_ColorIncrement; // Use color without subtraction due to rounding. int delta = compareColor - index * LT2_ColorIncrement; if ((delta < LT2_PixelTestThreshold) && (delta > -LT2_PixelTestThreshold)) { *readbackIndex = index; return true; } return false; } static unsigned char ReadbackIndexToColor(int readbackIndex) { OVR_ASSERT(readbackIndex < LT2_IncrementCount); return (unsigned char)(readbackIndex * LT2_ColorIncrement + LT2_ColorIncrement/2); } }; // FrameTimeRecordSet is a container holding multiple consecutive frame timing records // returned from the lock-less state. Used by FrameTimeManager. struct FrameTimeRecordSet { enum { RecordCount = 4, RecordMask = RecordCount - 1 }; FrameTimeRecord Records[RecordCount]; int NextWriteIndex; FrameTimeRecordSet() { NextWriteIndex = 0; memset(this, 0, sizeof(FrameTimeRecordSet)); } void AddValue(int readValue, double timeSeconds) { Records[NextWriteIndex].ReadbackIndex = readValue; Records[NextWriteIndex].TimeSeconds = timeSeconds; NextWriteIndex ++; if (NextWriteIndex == RecordCount) NextWriteIndex = 0; } // Matching should be done starting from NextWrite index // until wrap-around const FrameTimeRecord& operator [] (int i) const { return Records[(NextWriteIndex + i) & RecordMask]; } const FrameTimeRecord& GetMostRecentFrame() { return Records[(NextWriteIndex - 1) & RecordMask]; } // Advances I to absolute color index bool FindReadbackIndex(int* i, int readbackIndex) const { for (; *i < RecordCount; (*i)++) { if ((*this)[*i].ReadbackIndex == readbackIndex) return true; } return false; } bool IsAllZeroes() const { for (int i = 0; i < RecordCount; i++) if (Records[i].ReadbackIndex != 0) return false; return true; } }; //------------------------------------------------------------------------------------- // ***** LatencyTest2 // // LatencyTest2 utility class wraps the low level SensorDevice and manages the scheduling // of a latency test. A single test is composed of a series of individual latency measurements // which are used to derive min, max, and an average latency value. // // Developers are required to call the following methods: // SetDevice - Sets the SensorDevice to be used for the tests. // ProcessInputs - This should be called at the same place in the code where the game engine // reads the headset orientation from LibOVR (typically done by calling // 'GetOrientation' on the SensorFusion object). Calling this at the right time // enables us to measure the same latency that occurs for headset orientation // changes. // DisplayScreenColor - The latency tester works by sensing the color of the pixels directly // beneath it. The color of these pixels can be set by drawing a small // quad at the end of the rendering stage. The quad should be small // such that it doesn't significantly impact the rendering of the scene, // but large enough to be 'seen' by the sensor. See the SDK // documentation for more information. // GetResultsString - Call this to get a string containing the most recent results. // If the string has already been gotten then NULL will be returned. // The string pointer will remain valid until the next time this // method is called. // class LatencyTest2 : public NewOverrideBase { public: LatencyTest2(SensorDevice* device = NULL); ~LatencyTest2(); // Set the Latency Tester device that we'll use to send commands to and receive // notification messages from. bool SetSensorDevice(SensorDevice* device); bool SetDisplayDevice(LatencyTestDevice* device); // Returns true if this LatencyTestUtil has a Latency Tester device. bool HasDisplayDevice() const { return LatencyTesterDev.GetPtr() != NULL; } bool HasDevice() const { return Handler.IsHandlerInstalled(); } bool DisplayScreenColor(Color& colorToDisplay); //const char* GetResultsString(); // Begin test. Equivalent to pressing the button on the latency tester. void BeginTest(double startTime = -1.0f); bool IsMeasuringNow() const { return TestActive; } double GetMeasuredLatency() const { return LatencyMeasuredInSeconds; } // FrameTimeRecordSet GetLocklessState() { return LockessRecords.GetState(); } private: LatencyTest2* getThis() { return this; } enum LatencyTestMessageType { LatencyTest_None, LatencyTest_Timer, LatencyTest_ProcessInputs, }; void handleMessage(const MessagePixelRead& msg); class PixelReadHandler : public MessageHandler { LatencyTest2* pLatencyTestUtil; public: PixelReadHandler(LatencyTest2* latencyTester) : pLatencyTestUtil(latencyTester) { } ~PixelReadHandler(); virtual void OnMessage(const Message& msg); }; PixelReadHandler Handler; Ptr HmdDevice; Ptr LatencyTesterDev; Lock TesterLock; bool TestActive; unsigned char RenderColorValue; MessagePixelRead LastPixelReadMsg; double StartTiming; unsigned int RawStartTiming; UInt32 RawLatencyMeasured; double LatencyMeasuredInSeconds; int NumMsgsBeforeSettle; unsigned int NumTestsSuccessful; // MA: // Frames are added here, then copied into lockess state FrameTimeRecordSet RecentFrameSet; LocklessUpdater LockessRecords; }; }} // namespace OVR::Util #endif // OVR_Util_LatencyTest2_h \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Render_Stereo.cpp b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Render_Stereo.cpp new file mode 100644 index 0000000..a3bc669 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Render_Stereo.cpp @@ -0,0 +1 @@ +/************************************************************************************ Filename : Util_Render_Stereo.cpp Content : Stereo rendering configuration implementation Created : October 22, 2012 Authors : Michael Antonov, Andrew Reisse, Tom Forsyth Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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 "Util_Render_Stereo.h" #include "../OVR_SensorFusion.h" namespace OVR { namespace Util { namespace Render { //----------------------------------------------------------------------------------- // **** Useful debug functions. char const* GetDebugNameEyeCupType ( EyeCupType eyeCupType ) { switch ( eyeCupType ) { case EyeCup_DK1A: return "DK1 A"; break; case EyeCup_DK1B: return "DK1 B"; break; case EyeCup_DK1C: return "DK1 C"; break; case EyeCup_DKHD2A: return "DKHD2 A"; break; case EyeCup_OrangeA: return "Orange A"; break; case EyeCup_RedA: return "Red A"; break; case EyeCup_PinkA: return "Pink A"; break; case EyeCup_BlueA: return "Blue A"; break; case EyeCup_Delilah1A: return "Delilah 1 A"; break; case EyeCup_Delilah2A: return "Delilah 2 A"; break; case EyeCup_JamesA: return "James A"; break; case EyeCup_SunMandalaA: return "Sun Mandala A"; break; case EyeCup_DK2A: return "DK2 A"; break; case EyeCup_LAST: return "LAST"; break; default: OVR_ASSERT ( false ); return "Error"; break; } } char const* GetDebugNameHmdType ( HmdTypeEnum hmdType ) { switch ( hmdType ) { case HmdType_None: return "None"; break; case HmdType_DK1: return "DK1"; break; case HmdType_DKProto: return "DK1 prototype"; break; case HmdType_DKHDProto: return "DK HD prototype 1"; break; case HmdType_DKHDProto566Mi: return "DK HD prototype 566 Mi"; break; case HmdType_DKHD2Proto: return "DK HD prototype 585"; break; case HmdType_CrystalCoveProto: return "Crystal Cove"; break; case HmdType_DK2: return "DK2"; break; case HmdType_Unknown: return "Unknown"; break; case HmdType_LAST: return "LAST"; break; default: OVR_ASSERT ( false ); return "Error"; break; } } //----------------------------------------------------------------------------------- // **** Internal pipeline functions. struct DistortionAndFov { DistortionRenderDesc Distortion; FovPort Fov; }; static DistortionAndFov CalculateDistortionAndFovInternal ( StereoEye eyeType, HmdRenderInfo const &hmd, LensConfig const *pLensOverride = NULL, FovPort const *pTanHalfFovOverride = NULL, float extraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION ) { // pLensOverride can be NULL, which means no override. DistortionRenderDesc localDistortion = CalculateDistortionRenderDesc ( eyeType, hmd, pLensOverride ); FovPort fov = CalculateFovFromHmdInfo ( eyeType, localDistortion, hmd, extraEyeRotationInRadians ); // Here the app or the user would optionally clamp this visible fov to a smaller number if // they want more perf or resolution and are willing to give up FOV. // They may also choose to clamp UDLR differently e.g. to get cinemascope-style views. if ( pTanHalfFovOverride != NULL ) { fov = *pTanHalfFovOverride; } // Here we could call ClampToPhysicalScreenFov(), but we do want people // to be able to play with larger-than-screen views. // The calling app can always do the clamping itself. DistortionAndFov result; result.Distortion = localDistortion; result.Fov = fov; return result; } static Recti CalculateViewportInternal ( StereoEye eyeType, Sizei const actualRendertargetSurfaceSize, Sizei const requestedRenderedPixelSize, bool bRendertargetSharedByBothEyes, bool bMonoRenderingMode = false ) { Recti renderedViewport; if ( bMonoRenderingMode || !bRendertargetSharedByBothEyes || (eyeType == StereoEye_Center) ) { // One eye per RT. renderedViewport.x = 0; renderedViewport.y = 0; renderedViewport.w = Alg::Min ( actualRendertargetSurfaceSize.w, requestedRenderedPixelSize.w ); renderedViewport.h = Alg::Min ( actualRendertargetSurfaceSize.h, requestedRenderedPixelSize.h ); } else { // Both eyes share the RT. renderedViewport.x = 0; renderedViewport.y = 0; renderedViewport.w = Alg::Min ( actualRendertargetSurfaceSize.w/2, requestedRenderedPixelSize.w ); renderedViewport.h = Alg::Min ( actualRendertargetSurfaceSize.h, requestedRenderedPixelSize.h ); if ( eyeType == StereoEye_Right ) { renderedViewport.x = (actualRendertargetSurfaceSize.w+1)/2; // Round up, not down. } } return renderedViewport; } static Recti CalculateViewportDensityInternal ( StereoEye eyeType, DistortionRenderDesc const &distortion, FovPort const &fov, Sizei const &actualRendertargetSurfaceSize, bool bRendertargetSharedByBothEyes, float desiredPixelDensity = 1.0f, bool bMonoRenderingMode = false ) { OVR_ASSERT ( actualRendertargetSurfaceSize.w > 0 ); OVR_ASSERT ( actualRendertargetSurfaceSize.h > 0 ); // What size RT do we need to get 1:1 mapping? Sizei idealPixelSize = CalculateIdealPixelSize ( eyeType, distortion, fov, desiredPixelDensity ); // ...but we might not actually get that size. return CalculateViewportInternal ( eyeType, actualRendertargetSurfaceSize, idealPixelSize, bRendertargetSharedByBothEyes, bMonoRenderingMode ); } static ViewportScaleAndOffset CalculateViewportScaleAndOffsetInternal ( ScaleAndOffset2D const &eyeToSourceNDC, Recti const &renderedViewport, Sizei const &actualRendertargetSurfaceSize ) { ViewportScaleAndOffset result; result.RenderedViewport = renderedViewport; result.EyeToSourceUV = CreateUVScaleAndOffsetfromNDCScaleandOffset( eyeToSourceNDC, renderedViewport, actualRendertargetSurfaceSize ); return result; } static StereoEyeParams CalculateStereoEyeParamsInternal ( StereoEye eyeType, HmdRenderInfo const &hmd, DistortionRenderDesc const &distortion, FovPort const &fov, Sizei const &actualRendertargetSurfaceSize, Recti const &renderedViewport, bool bRightHanded = true, float zNear = 0.01f, float zFar = 10000.0f, bool bMonoRenderingMode = false, float zoomFactor = 1.0f ) { // Generate the projection matrix for intermediate rendertarget. // Z range can also be inserted later by the app (though not in this particular case) float fovScale = 1.0f / zoomFactor; FovPort zoomedFov = fov; zoomedFov.LeftTan *= fovScale; zoomedFov.RightTan *= fovScale; zoomedFov.UpTan *= fovScale; zoomedFov.DownTan *= fovScale; Matrix4f projection = CreateProjection ( bRightHanded, zoomedFov, zNear, zFar ); // Find the mapping from TanAngle space to target NDC space. // Note this does NOT take the zoom factor into account because // this is the mapping of actual physical eye FOV (and our eyes do not zoom!) // to screen space. ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov ( fov ); // The size of the final FB, which is fixed and determined by the physical size of the device display. Recti distortedViewport = GetFramebufferViewport ( eyeType, hmd ); Vector3f virtualCameraOffset = CalculateEyeVirtualCameraOffset(hmd, eyeType, bMonoRenderingMode); StereoEyeParams result; result.Eye = eyeType; result.ViewAdjust = Matrix4f::Translation(virtualCameraOffset); result.Distortion = distortion; result.DistortionViewport = distortedViewport; result.Fov = fov; result.RenderedProjection = projection; result.EyeToSourceNDC = eyeToSourceNDC; ViewportScaleAndOffset vsao = CalculateViewportScaleAndOffsetInternal ( eyeToSourceNDC, renderedViewport, actualRendertargetSurfaceSize ); result.RenderedViewport = vsao.RenderedViewport; result.EyeToSourceUV = vsao.EyeToSourceUV; return result; } Vector3f CalculateEyeVirtualCameraOffset(HmdRenderInfo const &hmd, StereoEye eyeType, bool bmonoRenderingMode) { Vector3f virtualCameraOffset(0); if (!bmonoRenderingMode) { float eyeCenterRelief = hmd.GetEyeCenter().ReliefInMeters; if (eyeType == StereoEye_Left) { virtualCameraOffset.x = hmd.EyeLeft.NoseToPupilInMeters; virtualCameraOffset.z = eyeCenterRelief - hmd.EyeLeft.ReliefInMeters; } else if (eyeType == StereoEye_Right) { virtualCameraOffset.x = -hmd.EyeRight.NoseToPupilInMeters; virtualCameraOffset.z = eyeCenterRelief - hmd.EyeRight.ReliefInMeters; } } return virtualCameraOffset; } //----------------------------------------------------------------------------------- // **** Higher-level utility functions. Sizei CalculateRecommendedTextureSize ( HmdRenderInfo const &hmd, bool bRendertargetSharedByBothEyes, float pixelDensityInCenter /*= 1.0f*/ ) { Sizei idealPixelSize[2]; for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { StereoEye eyeType = ( eyeNum == 0 ) ? StereoEye_Left : StereoEye_Right; DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, hmd, NULL, NULL, OVR_DEFAULT_EXTRA_EYE_ROTATION ); idealPixelSize[eyeNum] = CalculateIdealPixelSize ( eyeType, distortionAndFov.Distortion, distortionAndFov.Fov, pixelDensityInCenter ); } Sizei result; result.w = Alg::Max ( idealPixelSize[0].w, idealPixelSize[1].w ); result.h = Alg::Max ( idealPixelSize[0].h, idealPixelSize[1].h ); if ( bRendertargetSharedByBothEyes ) { result.w *= 2; } return result; } StereoEyeParams CalculateStereoEyeParams ( HmdRenderInfo const &hmd, StereoEye eyeType, Sizei const &actualRendertargetSurfaceSize, bool bRendertargetSharedByBothEyes, bool bRightHanded /*= true*/, float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/, Sizei const *pOverrideRenderedPixelSize /* = NULL*/, FovPort const *pOverrideFovport /*= NULL*/, float zoomFactor /*= 1.0f*/ ) { DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, hmd, NULL, NULL, OVR_DEFAULT_EXTRA_EYE_ROTATION ); if ( pOverrideFovport != NULL ) { distortionAndFov.Fov = *pOverrideFovport; } Recti viewport; if ( pOverrideRenderedPixelSize != NULL ) { viewport = CalculateViewportInternal ( eyeType, actualRendertargetSurfaceSize, *pOverrideRenderedPixelSize, bRendertargetSharedByBothEyes, false ); } else { viewport = CalculateViewportDensityInternal ( eyeType, distortionAndFov.Distortion, distortionAndFov.Fov, actualRendertargetSurfaceSize, bRendertargetSharedByBothEyes, 1.0f, false ); } return CalculateStereoEyeParamsInternal ( eyeType, hmd, distortionAndFov.Distortion, distortionAndFov.Fov, actualRendertargetSurfaceSize, viewport, bRightHanded, zNear, zFar, false, zoomFactor ); } FovPort CalculateRecommendedFov ( HmdRenderInfo const &hmd, StereoEye eyeType, bool bMakeFovSymmetrical /* = false */ ) { DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, hmd, NULL, NULL, OVR_DEFAULT_EXTRA_EYE_ROTATION ); FovPort fov = distortionAndFov.Fov; if ( bMakeFovSymmetrical ) { // Deal with engines that cannot support an off-center projection. // Unfortunately this means they will be rendering pixels that the user can't actually see. float fovTanH = Alg::Max ( fov.LeftTan, fov.RightTan ); float fovTanV = Alg::Max ( fov.UpTan, fov.DownTan ); fov.LeftTan = fovTanH; fov.RightTan = fovTanH; fov.UpTan = fovTanV; fov.DownTan = fovTanV; } return fov; } ViewportScaleAndOffset ModifyRenderViewport ( StereoEyeParams const ¶ms, Sizei const &actualRendertargetSurfaceSize, Recti const &renderViewport ) { return CalculateViewportScaleAndOffsetInternal ( params.EyeToSourceNDC, renderViewport, actualRendertargetSurfaceSize ); } ViewportScaleAndOffset ModifyRenderSize ( StereoEyeParams const ¶ms, Sizei const &actualRendertargetSurfaceSize, Sizei const &requestedRenderSize, bool bRendertargetSharedByBothEyes /*= false*/ ) { Recti renderViewport = CalculateViewportInternal ( params.Eye, actualRendertargetSurfaceSize, requestedRenderSize, bRendertargetSharedByBothEyes, false ); return CalculateViewportScaleAndOffsetInternal ( params.EyeToSourceNDC, renderViewport, actualRendertargetSurfaceSize ); } ViewportScaleAndOffset ModifyRenderDensity ( StereoEyeParams const ¶ms, Sizei const &actualRendertargetSurfaceSize, float pixelDensity /*= 1.0f*/, bool bRendertargetSharedByBothEyes /*= false*/ ) { Recti renderViewport = CalculateViewportDensityInternal ( params.Eye, params.Distortion, params.Fov, actualRendertargetSurfaceSize, bRendertargetSharedByBothEyes, pixelDensity, false ); return CalculateViewportScaleAndOffsetInternal ( params.EyeToSourceNDC, renderViewport, actualRendertargetSurfaceSize ); } //----------------------------------------------------------------------------------- // **** StereoConfig Implementation StereoConfig::StereoConfig(StereoMode mode) : Mode(mode), DirtyFlag(true) { // Initialize "fake" default HMD values for testing without HMD plugged in. // These default values match those returned by DK1 // (at least they did at time of writing - certainly good enough for debugging) Hmd.HmdType = HmdType_None; Hmd.ResolutionInPixels = Sizei(1280, 800); Hmd.ScreenSizeInMeters = Sizef(0.1498f, 0.0936f); Hmd.ScreenGapSizeInMeters = 0.0f; Hmd.CenterFromTopInMeters = 0.0468f; Hmd.LensSeparationInMeters = 0.0635f; Hmd.LensDiameterInMeters = 0.035f; Hmd.LensSurfaceToMidplateInMeters = 0.025f; Hmd.EyeCups = EyeCup_DK1A; Hmd.Shutter.Type = HmdShutter_RollingTopToBottom; Hmd.Shutter.VsyncToNextVsync = ( 1.0f / 60.0f ); Hmd.Shutter.VsyncToFirstScanline = 0.000052f; Hmd.Shutter.FirstScanlineToLastScanline = 0.016580f; Hmd.Shutter.PixelSettleTime = 0.015f; Hmd.Shutter.PixelPersistence = ( 1.0f / 60.0f ); Hmd.EyeLeft.Distortion.SetToIdentity(); Hmd.EyeLeft.Distortion.MetersPerTanAngleAtCenter = 0.043875f; Hmd.EyeLeft.Distortion.Eqn = Distortion_RecipPoly4; Hmd.EyeLeft.Distortion.K[0] = 1.0f; Hmd.EyeLeft.Distortion.K[1] = -0.3999f; Hmd.EyeLeft.Distortion.K[2] = 0.2408f; Hmd.EyeLeft.Distortion.K[3] = -0.4589f; Hmd.EyeLeft.Distortion.MaxR = 1.0f; Hmd.EyeLeft.Distortion.ChromaticAberration[0] = 0.006f; Hmd.EyeLeft.Distortion.ChromaticAberration[1] = 0.0f; Hmd.EyeLeft.Distortion.ChromaticAberration[2] = -0.014f; Hmd.EyeLeft.Distortion.ChromaticAberration[3] = 0.0f; Hmd.EyeLeft.NoseToPupilInMeters = 0.62f; Hmd.EyeLeft.ReliefInMeters = 0.013f; Hmd.EyeRight = Hmd.EyeLeft; SetViewportMode = SVPM_Density; SetViewportPixelsPerDisplayPixel = 1.0f; // Not used in this mode, but init them anyway. SetViewportSize[0] = Sizei(0,0); SetViewportSize[1] = Sizei(0,0); SetViewport[0] = Recti(0,0,0,0); SetViewport[1] = Recti(0,0,0,0); OverrideLens = false; OverrideTanHalfFov = false; OverrideZeroIpd = false; ExtraEyeRotationInRadians = OVR_DEFAULT_EXTRA_EYE_ROTATION; IsRendertargetSharedByBothEyes = true; RightHandedProjection = true; // This should cause an assert if the app does not call SetRendertargetSize() RendertargetSize = Sizei ( 0, 0 ); ZNear = 0.01f; ZFar = 10000.0f; Set2DAreaFov(DegreeToRad(85.0f)); } void StereoConfig::SetHmdRenderInfo(const HmdRenderInfo& hmd) { Hmd = hmd; DirtyFlag = true; } void StereoConfig::Set2DAreaFov(float fovRadians) { Area2DFov = fovRadians; DirtyFlag = true; } const StereoEyeParamsWithOrtho& StereoConfig::GetEyeRenderParams(StereoEye eye) { if ( DirtyFlag ) { UpdateComputedState(); } static const UByte eyeParamIndices[3] = { 0, 0, 1 }; OVR_ASSERT(eye < sizeof(eyeParamIndices)); return EyeRenderParams[eyeParamIndices[eye]]; } void StereoConfig::SetLensOverride ( LensConfig const *pLensOverrideLeft /*= NULL*/, LensConfig const *pLensOverrideRight /*= NULL*/ ) { if ( pLensOverrideLeft == NULL ) { OverrideLens = false; } else { OverrideLens = true; LensOverrideLeft = *pLensOverrideLeft; LensOverrideRight = *pLensOverrideLeft; if ( pLensOverrideRight != NULL ) { LensOverrideRight = *pLensOverrideRight; } } DirtyFlag = true; } void StereoConfig::SetRendertargetSize (Size const rendertargetSize, bool rendertargetIsSharedByBothEyes ) { RendertargetSize = rendertargetSize; IsRendertargetSharedByBothEyes = rendertargetIsSharedByBothEyes; DirtyFlag = true; } void StereoConfig::SetFov ( FovPort const *pfovLeft /*= NULL*/, FovPort const *pfovRight /*= NULL*/ ) { DirtyFlag = true; if ( pfovLeft == NULL ) { OverrideTanHalfFov = false; } else { OverrideTanHalfFov = true; FovOverrideLeft = *pfovLeft; FovOverrideRight = *pfovLeft; if ( pfovRight != NULL ) { FovOverrideRight = *pfovRight; } } } void StereoConfig::SetZeroVirtualIpdOverride ( bool enableOverride ) { DirtyFlag = true; OverrideZeroIpd = enableOverride; } void StereoConfig::SetZClipPlanesAndHandedness ( float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/, bool rightHandedProjection /*= true*/ ) { DirtyFlag = true; ZNear = zNear; ZFar = zFar; RightHandedProjection = rightHandedProjection; } void StereoConfig::SetExtraEyeRotation ( float extraEyeRotationInRadians ) { DirtyFlag = true; ExtraEyeRotationInRadians = extraEyeRotationInRadians; } Sizei StereoConfig::CalculateRecommendedTextureSize ( bool rendertargetSharedByBothEyes, float pixelDensityInCenter /*= 1.0f*/ ) { return Render::CalculateRecommendedTextureSize ( Hmd, rendertargetSharedByBothEyes, pixelDensityInCenter ); } void StereoConfig::UpdateComputedState() { int numEyes = 2; StereoEye eyeTypes[2]; switch ( Mode ) { case Stereo_None: numEyes = 1; eyeTypes[0] = StereoEye_Center; break; case Stereo_LeftRight_Multipass: numEyes = 2; eyeTypes[0] = StereoEye_Left; eyeTypes[1] = StereoEye_Right; break; default: OVR_ASSERT( false ); break; } // If either of these fire, you've probably forgotten to call SetRendertargetSize() OVR_ASSERT ( RendertargetSize.w > 0 ); OVR_ASSERT ( RendertargetSize.h > 0 ); for ( int eyeNum = 0; eyeNum < numEyes; eyeNum++ ) { StereoEye eyeType = eyeTypes[eyeNum]; LensConfig *pLensOverride = NULL; if ( OverrideLens ) { if ( eyeType == StereoEye_Right ) { pLensOverride = &LensOverrideRight; } else { pLensOverride = &LensOverrideLeft; } } FovPort *pTanHalfFovOverride = NULL; if ( OverrideTanHalfFov ) { if ( eyeType == StereoEye_Right ) { pTanHalfFovOverride = &FovOverrideRight; } else { pTanHalfFovOverride = &FovOverrideLeft; } } DistortionAndFov distortionAndFov = CalculateDistortionAndFovInternal ( eyeType, Hmd, pLensOverride, pTanHalfFovOverride, ExtraEyeRotationInRadians ); EyeRenderParams[eyeNum].StereoEye.Distortion = distortionAndFov.Distortion; EyeRenderParams[eyeNum].StereoEye.Fov = distortionAndFov.Fov; } if ( OverrideZeroIpd ) { // Take the union of the calculated eye FOVs. FovPort fov; fov.UpTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.UpTan , EyeRenderParams[1].StereoEye.Fov.UpTan ); fov.DownTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.DownTan , EyeRenderParams[1].StereoEye.Fov.DownTan ); fov.LeftTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.LeftTan , EyeRenderParams[1].StereoEye.Fov.LeftTan ); fov.RightTan = Alg::Max ( EyeRenderParams[0].StereoEye.Fov.RightTan, EyeRenderParams[1].StereoEye.Fov.RightTan ); EyeRenderParams[0].StereoEye.Fov = fov; EyeRenderParams[1].StereoEye.Fov = fov; } for ( int eyeNum = 0; eyeNum < numEyes; eyeNum++ ) { StereoEye eyeType = eyeTypes[eyeNum]; DistortionRenderDesc localDistortion = EyeRenderParams[eyeNum].StereoEye.Distortion; FovPort fov = EyeRenderParams[eyeNum].StereoEye.Fov; // Use a placeholder - will be overridden later. Recti tempViewport = Recti ( 0, 0, 1, 1 ); EyeRenderParams[eyeNum].StereoEye = CalculateStereoEyeParamsInternal ( eyeType, Hmd, localDistortion, fov, RendertargetSize, tempViewport, RightHandedProjection, ZNear, ZFar, OverrideZeroIpd ); // We want to create a virtual 2D surface we can draw debug text messages to. // We'd like it to be a fixed distance (OrthoDistance) away, // and to cover a specific FOV (Area2DFov). We need to find the projection matrix for this, // and also to know how large it is in pixels to achieve a 1:1 mapping at the center of the screen. float orthoDistance = 0.8f; float orthoHalfFov = tanf ( Area2DFov * 0.5f ); Vector2f unityOrthoPixelSize = localDistortion.PixelsPerTanAngleAtCenter * ( orthoHalfFov * 2.0f ); float localInterpupillaryDistance = Hmd.EyeLeft.NoseToPupilInMeters + Hmd.EyeRight.NoseToPupilInMeters; if ( OverrideZeroIpd ) { localInterpupillaryDistance = 0.0f; } Matrix4f ortho = CreateOrthoSubProjection ( true, eyeType, orthoHalfFov, orthoHalfFov, unityOrthoPixelSize.x, unityOrthoPixelSize.y, orthoDistance, localInterpupillaryDistance, EyeRenderParams[eyeNum].StereoEye.RenderedProjection ); EyeRenderParams[eyeNum].OrthoProjection = ortho; } // ...and now set up the viewport, scale & offset the way the app wanted. setupViewportScaleAndOffsets(); if ( OverrideZeroIpd ) { // Monocular rendering has some fragile parts... don't break any by accident. OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.UpTan == EyeRenderParams[1].StereoEye.Fov.UpTan ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.DownTan == EyeRenderParams[1].StereoEye.Fov.DownTan ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.LeftTan == EyeRenderParams[1].StereoEye.Fov.LeftTan ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.Fov.RightTan == EyeRenderParams[1].StereoEye.Fov.RightTan ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[0][0] == EyeRenderParams[1].StereoEye.RenderedProjection.M[0][0] ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[1][1] == EyeRenderParams[1].StereoEye.RenderedProjection.M[1][1] ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[0][2] == EyeRenderParams[1].StereoEye.RenderedProjection.M[0][2] ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedProjection.M[1][2] == EyeRenderParams[1].StereoEye.RenderedProjection.M[1][2] ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.RenderedViewport == EyeRenderParams[1].StereoEye.RenderedViewport ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceUV.Offset == EyeRenderParams[1].StereoEye.EyeToSourceUV.Offset ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceUV.Scale == EyeRenderParams[1].StereoEye.EyeToSourceUV.Scale ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceNDC.Offset == EyeRenderParams[1].StereoEye.EyeToSourceNDC.Offset ); OVR_ASSERT ( EyeRenderParams[0].StereoEye.EyeToSourceNDC.Scale == EyeRenderParams[1].StereoEye.EyeToSourceNDC.Scale ); OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[0][0] == EyeRenderParams[1].OrthoProjection.M[0][0] ); OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[1][1] == EyeRenderParams[1].OrthoProjection.M[1][1] ); OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[0][2] == EyeRenderParams[1].OrthoProjection.M[0][2] ); OVR_ASSERT ( EyeRenderParams[0].OrthoProjection.M[1][2] == EyeRenderParams[1].OrthoProjection.M[1][2] ); } DirtyFlag = false; } ViewportScaleAndOffsetBothEyes StereoConfig::setupViewportScaleAndOffsets() { for ( int eyeNum = 0; eyeNum < 2; eyeNum++ ) { StereoEye eyeType = ( eyeNum == 0 ) ? StereoEye_Left : StereoEye_Right; DistortionRenderDesc localDistortion = EyeRenderParams[eyeNum].StereoEye.Distortion; FovPort fov = EyeRenderParams[eyeNum].StereoEye.Fov; Recti renderedViewport; switch ( SetViewportMode ) { case SVPM_Density: renderedViewport = CalculateViewportDensityInternal ( eyeType, localDistortion, fov, RendertargetSize, IsRendertargetSharedByBothEyes, SetViewportPixelsPerDisplayPixel, OverrideZeroIpd ); break; case SVPM_Size: if ( ( eyeType == StereoEye_Right ) && !OverrideZeroIpd ) { renderedViewport = CalculateViewportInternal ( eyeType, RendertargetSize, SetViewportSize[1], IsRendertargetSharedByBothEyes, OverrideZeroIpd ); } else { renderedViewport = CalculateViewportInternal ( eyeType, RendertargetSize, SetViewportSize[0], IsRendertargetSharedByBothEyes, OverrideZeroIpd ); } break; case SVPM_Viewport: if ( ( eyeType == StereoEye_Right ) && !OverrideZeroIpd ) { renderedViewport = SetViewport[1]; } else { renderedViewport = SetViewport[0]; } break; default: OVR_ASSERT ( false ); break; } ViewportScaleAndOffset vpsao = CalculateViewportScaleAndOffsetInternal ( EyeRenderParams[eyeNum].StereoEye.EyeToSourceNDC, renderedViewport, RendertargetSize ); EyeRenderParams[eyeNum].StereoEye.RenderedViewport = vpsao.RenderedViewport; EyeRenderParams[eyeNum].StereoEye.EyeToSourceUV = vpsao.EyeToSourceUV; } ViewportScaleAndOffsetBothEyes result; result.Left.EyeToSourceUV = EyeRenderParams[0].StereoEye.EyeToSourceUV; result.Left.RenderedViewport = EyeRenderParams[0].StereoEye.RenderedViewport; result.Right.EyeToSourceUV = EyeRenderParams[1].StereoEye.EyeToSourceUV; result.Right.RenderedViewport = EyeRenderParams[1].StereoEye.RenderedViewport; return result; } // Specify a pixel density - how many rendered pixels per pixel in the physical display. ViewportScaleAndOffsetBothEyes StereoConfig::SetRenderDensity ( float pixelsPerDisplayPixel ) { SetViewportMode = SVPM_Density; SetViewportPixelsPerDisplayPixel = pixelsPerDisplayPixel; return setupViewportScaleAndOffsets(); } // Supply the size directly. Will be clamped to the physical rendertarget size. ViewportScaleAndOffsetBothEyes StereoConfig::SetRenderSize ( Sizei const &renderSizeLeft, Sizei const &renderSizeRight ) { SetViewportMode = SVPM_Size; SetViewportSize[0] = renderSizeLeft; SetViewportSize[1] = renderSizeRight; return setupViewportScaleAndOffsets(); } // Supply the viewport directly. This is not clamped to the physical rendertarget - careful now! ViewportScaleAndOffsetBothEyes StereoConfig::SetRenderViewport ( Recti const &renderViewportLeft, Recti const &renderViewportRight ) { SetViewportMode = SVPM_Viewport; SetViewport[0] = renderViewportLeft; SetViewport[1] = renderViewportRight; return setupViewportScaleAndOffsets(); } Matrix4f StereoConfig::GetProjectionWithZoom ( StereoEye eye, float fovZoom ) const { int eyeNum = ( eye == StereoEye_Right ) ? 1 : 0; float fovScale = 1.0f / fovZoom; FovPort fovPort = EyeRenderParams[eyeNum].StereoEye.Fov; fovPort.LeftTan *= fovScale; fovPort.RightTan *= fovScale; fovPort.UpTan *= fovScale; fovPort.DownTan *= fovScale; return CreateProjection ( RightHandedProjection, fovPort, ZNear, ZFar ); } //----------------------------------------------------------------------------------- // ***** Distortion Mesh Rendering // Pow2 for the Morton order to work! // 4 is too low - it is easy to see the "wobbles" in the HMD. // 5 is realllly close but you can see pixel differences with even/odd frame checking. // 6 is indistinguishable on a monitor on even/odd frames. static const int DMA_GridSizeLog2 = 6; static const int DMA_GridSize = 1<TanEyeAnglesR = tanEyeAnglesR; pcurVert->TanEyeAnglesG = tanEyeAnglesG; pcurVert->TanEyeAnglesB = tanEyeAnglesB; HmdShutterTypeEnum shutterType = hmdRenderInfo.Shutter.Type; switch ( shutterType ) { case HmdShutter_Global: pcurVert->TimewarpLerp = 0.0f; break; case HmdShutter_RollingLeftToRight: // Retrace is left to right - left eye goes 0.0 -> 0.5, then right goes 0.5 -> 1.0 pcurVert->TimewarpLerp = screenNDC.x * 0.25f + 0.25f; if (rightEye) { pcurVert->TimewarpLerp += 0.5f; } break; case HmdShutter_RollingRightToLeft: // Retrace is right to left - right eye goes 0.0 -> 0.5, then left goes 0.5 -> 1.0 pcurVert->TimewarpLerp = 0.75f - screenNDC.x * 0.25f; if (rightEye) { pcurVert->TimewarpLerp -= 0.5f; } break; case HmdShutter_RollingTopToBottom: // Retrace is top to bottom on both eyes at the same time. pcurVert->TimewarpLerp = screenNDC.y * 0.5f + 0.5f; break; default: OVR_ASSERT ( false ); break; } // Fade out at texture edges. float edgeFadeIn = ( 1.0f / fadeOutBorderFraction ) * ( 1.0f - Alg::Max ( Alg::Abs ( sourceCoordNDC.x ), Alg::Abs ( sourceCoordNDC.y ) ) ); // Also fade out at screen edges. float edgeFadeInScreen = ( 2.0f / fadeOutBorderFraction ) * ( 1.0f - Alg::Max ( Alg::Abs ( screenNDC.x ), Alg::Abs ( screenNDC.y ) ) ); edgeFadeIn = Alg::Min ( edgeFadeInScreen, edgeFadeIn ); // Don't let verts overlap to the other eye. screenNDC.x = Alg::Max ( -1.0f, Alg::Min ( screenNDC.x, 1.0f ) ); screenNDC.y = Alg::Max ( -1.0f, Alg::Min ( screenNDC.y, 1.0f ) ); pcurVert->Shade = Alg::Max ( 0.0f, Alg::Min ( edgeFadeIn, 1.0f ) ); pcurVert->ScreenPosNDC.x = 0.5f * screenNDC.x - 0.5f + xOffset; pcurVert->ScreenPosNDC.y = -screenNDC.y; pcurVert++; } } // Populate index buffer info UInt16 *pcurIndex = *ppTriangleListIndices; for ( int triNum = 0; triNum < DMA_GridSize * DMA_GridSize; triNum++ ) { // Use a Morton order to help locality of FB, texture and vertex cache. // (0.325ms raster order -> 0.257ms Morton order) OVR_ASSERT ( DMA_GridSize <= 256 ); int x = ( ( triNum & 0x0001 ) >> 0 ) | ( ( triNum & 0x0004 ) >> 1 ) | ( ( triNum & 0x0010 ) >> 2 ) | ( ( triNum & 0x0040 ) >> 3 ) | ( ( triNum & 0x0100 ) >> 4 ) | ( ( triNum & 0x0400 ) >> 5 ) | ( ( triNum & 0x1000 ) >> 6 ) | ( ( triNum & 0x4000 ) >> 7 ); int y = ( ( triNum & 0x0002 ) >> 1 ) | ( ( triNum & 0x0008 ) >> 2 ) | ( ( triNum & 0x0020 ) >> 3 ) | ( ( triNum & 0x0080 ) >> 4 ) | ( ( triNum & 0x0200 ) >> 5 ) | ( ( triNum & 0x0800 ) >> 6 ) | ( ( triNum & 0x2000 ) >> 7 ) | ( ( triNum & 0x8000 ) >> 8 ); int FirstVertex = x * (DMA_GridSize+1) + y; // Another twist - we want the top-left and bottom-right quadrants to // have the triangles split one way, the other two split the other. // +---+---+---+---+ // | /| /|\ |\ | // | / | / | \ | \ | // |/ |/ | \| \| // +---+---+---+---+ // | /| /|\ |\ | // | / | / | \ | \ | // |/ |/ | \| \| // +---+---+---+---+ // |\ |\ | /| /| // | \ | \ | / | / | // | \| \|/ |/ | // +---+---+---+---+ // |\ |\ | /| /| // | \ | \ | / | / | // | \| \|/ |/ | // +---+---+---+---+ // This way triangle edges don't span long distances over the distortion function, // so linear interpolation works better & we can use fewer tris. if ( ( x < DMA_GridSize/2 ) != ( y < DMA_GridSize/2 ) ) // != is logical XOR { *pcurIndex++ = (UInt16)FirstVertex; *pcurIndex++ = (UInt16)FirstVertex+1; *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1; *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1; *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1); *pcurIndex++ = (UInt16)FirstVertex; } else { *pcurIndex++ = (UInt16)FirstVertex; *pcurIndex++ = (UInt16)FirstVertex+1; *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1); *pcurIndex++ = (UInt16)FirstVertex+1; *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1)+1; *pcurIndex++ = (UInt16)FirstVertex+(DMA_GridSize+1); } } } //----------------------------------------------------------------------------------- // ***** Heightmap Mesh Rendering static const int HMA_GridSizeLog2 = 7; static const int HMA_GridSize = 1<TanEyeAngles = tanEyeAngle; HmdShutterTypeEnum shutterType = hmdRenderInfo.Shutter.Type; switch ( shutterType ) { case HmdShutter_Global: pcurVert->TimewarpLerp = 0.0f; break; case HmdShutter_RollingLeftToRight: // Retrace is left to right - left eye goes 0.0 -> 0.5, then right goes 0.5 -> 1.0 pcurVert->TimewarpLerp = sourceCoordNDC.x * 0.25f + 0.25f; if (rightEye) { pcurVert->TimewarpLerp += 0.5f; } break; case HmdShutter_RollingRightToLeft: // Retrace is right to left - right eye goes 0.0 -> 0.5, then left goes 0.5 -> 1.0 pcurVert->TimewarpLerp = 0.75f - sourceCoordNDC.x * 0.25f; if (rightEye) { pcurVert->TimewarpLerp -= 0.5f; } break; case HmdShutter_RollingTopToBottom: // Retrace is top to bottom on both eyes at the same time. pcurVert->TimewarpLerp = sourceCoordNDC.y * 0.5f + 0.5f; break; default: OVR_ASSERT ( false ); break; } // Don't let verts overlap to the other eye. //sourceCoordNDC.x = Alg::Max ( -1.0f, Alg::Min ( sourceCoordNDC.x, 1.0f ) ); //sourceCoordNDC.y = Alg::Max ( -1.0f, Alg::Min ( sourceCoordNDC.y, 1.0f ) ); //pcurVert->ScreenPosNDC.x = 0.5f * sourceCoordNDC.x - 0.5f + xOffset; pcurVert->ScreenPosNDC.x = sourceCoordNDC.x; pcurVert->ScreenPosNDC.y = -sourceCoordNDC.y; pcurVert++; } } // Populate index buffer info UInt16 *pcurIndex = *ppTriangleListIndices; for ( int triNum = 0; triNum < HMA_GridSize * HMA_GridSize; triNum++ ) { // Use a Morton order to help locality of FB, texture and vertex cache. // (0.325ms raster order -> 0.257ms Morton order) OVR_ASSERT ( HMA_GridSize < 256 ); int x = ( ( triNum & 0x0001 ) >> 0 ) | ( ( triNum & 0x0004 ) >> 1 ) | ( ( triNum & 0x0010 ) >> 2 ) | ( ( triNum & 0x0040 ) >> 3 ) | ( ( triNum & 0x0100 ) >> 4 ) | ( ( triNum & 0x0400 ) >> 5 ) | ( ( triNum & 0x1000 ) >> 6 ) | ( ( triNum & 0x4000 ) >> 7 ); int y = ( ( triNum & 0x0002 ) >> 1 ) | ( ( triNum & 0x0008 ) >> 2 ) | ( ( triNum & 0x0020 ) >> 3 ) | ( ( triNum & 0x0080 ) >> 4 ) | ( ( triNum & 0x0200 ) >> 5 ) | ( ( triNum & 0x0800 ) >> 6 ) | ( ( triNum & 0x2000 ) >> 7 ) | ( ( triNum & 0x8000 ) >> 8 ); int FirstVertex = x * (HMA_GridSize+1) + y; // Another twist - we want the top-left and bottom-right quadrants to // have the triangles split one way, the other two split the other. // +---+---+---+---+ // | /| /|\ |\ | // | / | / | \ | \ | // |/ |/ | \| \| // +---+---+---+---+ // | /| /|\ |\ | // | / | / | \ | \ | // |/ |/ | \| \| // +---+---+---+---+ // |\ |\ | /| /| // | \ | \ | / | / | // | \| \|/ |/ | // +---+---+---+---+ // |\ |\ | /| /| // | \ | \ | / | / | // | \| \|/ |/ | // +---+---+---+---+ // This way triangle edges don't span long distances over the distortion function, // so linear interpolation works better & we can use fewer tris. if ( ( x < HMA_GridSize/2 ) != ( y < HMA_GridSize/2 ) ) // != is logical XOR { *pcurIndex++ = (UInt16)FirstVertex; *pcurIndex++ = (UInt16)FirstVertex+1; *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1; *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1; *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1); *pcurIndex++ = (UInt16)FirstVertex; } else { *pcurIndex++ = (UInt16)FirstVertex; *pcurIndex++ = (UInt16)FirstVertex+1; *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1); *pcurIndex++ = (UInt16)FirstVertex+1; *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1)+1; *pcurIndex++ = (UInt16)FirstVertex+(HMA_GridSize+1); } } } //----------------------------------------------------------------------------------- // ***** Prediction and timewarp. // // Calculates the values from the HMD info. PredictionValues PredictionGetDeviceValues ( const HmdRenderInfo &hmdRenderInfo, bool withTimewarp /*= true*/, bool withVsync /*= true*/ ) { PredictionValues result; result.WithTimewarp = withTimewarp; result.WithVsync = withVsync; // For unclear reasons, most graphics systems add an extra frame of latency // somewhere along the way. In time we'll debug this and figure it out, but // for now this gets prediction a little bit better. const float extraFramesOfBufferingKludge = 1.0f; if ( withVsync ) { // These are the times from the Present+Flush to when the middle of the scene is "averagely visible" (without timewarp) // So if you had no timewarp, this, plus the time until the next vsync, is how much to predict by. result.PresentFlushToRenderedScene = extraFramesOfBufferingKludge * hmdRenderInfo.Shutter.FirstScanlineToLastScanline; // Predict to the middle of the screen being scanned out. result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.VsyncToFirstScanline + 0.5f * hmdRenderInfo.Shutter.FirstScanlineToLastScanline; // Time for pixels to get half-way to settling. result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.PixelSettleTime * 0.5f; // Predict to half-way through persistence result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.PixelPersistence * 0.5f; // The time from the Present+Flush to when the first scanline is "averagely visible". result.PresentFlushToTimewarpStart = extraFramesOfBufferingKludge * hmdRenderInfo.Shutter.FirstScanlineToLastScanline; // Predict to the first line being scanned out. result.PresentFlushToTimewarpStart += hmdRenderInfo.Shutter.VsyncToFirstScanline; // Time for pixels to get half-way to settling. result.PresentFlushToTimewarpStart += hmdRenderInfo.Shutter.PixelSettleTime * 0.5f; // Predict to half-way through persistence result.PresentFlushToTimewarpStart += hmdRenderInfo.Shutter.PixelPersistence * 0.5f; // Time to the the last scanline. result.PresentFlushToTimewarpEnd = result.PresentFlushToTimewarpStart + hmdRenderInfo.Shutter.FirstScanlineToLastScanline; // Ideal framerate. result.PresentFlushToPresentFlush = hmdRenderInfo.Shutter.VsyncToNextVsync; } else { // Timewarp without vsync is a little odd. // Currently, we assume that without vsync, we have no idea which scanline // is currently being sent to the display. So we can't do lerping timewarp, // we can just do a full-screen late-stage fixup. // "PresentFlushToRenderedScene" means the time from the Present+Flush to when the middle of the scene is "averagely visible" (without timewarp) // So if you had no timewarp, this, plus the time until the next flush (which is usually the time to render the frame), is how much to predict by. // Time for pixels to get half-way to settling. result.PresentFlushToRenderedScene = hmdRenderInfo.Shutter.PixelSettleTime * 0.5f; // Predict to half-way through persistence result.PresentFlushToRenderedScene += hmdRenderInfo.Shutter.PixelPersistence * 0.5f; // Without vsync, you don't know timings, and so can't do anything useful with lerped warping. result.PresentFlushToTimewarpStart = result.PresentFlushToRenderedScene; result.PresentFlushToTimewarpEnd = result.PresentFlushToRenderedScene; // There's no concept of "ideal" when vsync is off. result.PresentFlushToPresentFlush = 0.0f; } return result; } Matrix4f TimewarpComputePoseDelta ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust ) { Matrix4f worldFromPredictedView = (eyeViewAdjust * predictedViewFromWorld).InvertedHomogeneousTransform(); Matrix4f matRenderFromNowStart = (eyeViewAdjust * renderedViewFromWorld) * worldFromPredictedView; // The sensor-predicted orientations have: X=right, Y=up, Z=backwards. // The vectors inside the mesh are in NDC to keep the shader simple: X=right, Y=down, Z=forwards. // So we need to perform a similarity transform on this delta matrix. // The verbose code would look like this: /* Matrix4f matBasisChange; matBasisChange.SetIdentity(); matBasisChange.M[0][0] = 1.0f; matBasisChange.M[1][1] = -1.0f; matBasisChange.M[2][2] = -1.0f; Matrix4f matBasisChangeInv = matBasisChange.Inverted(); matRenderFromNow = matBasisChangeInv * matRenderFromNow * matBasisChange; */ // ...but of course all the above is a constant transform and much more easily done. // We flip the signs of the Y&Z row, then flip the signs of the Y&Z column, // and of course most of the flips cancel: // +++ +-- +-- // +++ -> flip Y&Z columns -> +-- -> flip Y&Z rows -> -++ // +++ +-- -++ matRenderFromNowStart.M[0][1] = -matRenderFromNowStart.M[0][1]; matRenderFromNowStart.M[0][2] = -matRenderFromNowStart.M[0][2]; matRenderFromNowStart.M[1][0] = -matRenderFromNowStart.M[1][0]; matRenderFromNowStart.M[2][0] = -matRenderFromNowStart.M[2][0]; matRenderFromNowStart.M[1][3] = -matRenderFromNowStart.M[1][3]; matRenderFromNowStart.M[2][3] = -matRenderFromNowStart.M[2][3]; return matRenderFromNowStart; } Matrix4f TimewarpComputePoseDeltaPosition ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust ) { Matrix4f worldFromPredictedView = (eyeViewAdjust * predictedViewFromWorld).InvertedHomogeneousTransform(); Matrix4f matRenderXform = (eyeViewAdjust * renderedViewFromWorld) * worldFromPredictedView; return matRenderXform.Inverted(); } TimewarpMachine::TimewarpMachine() { for ( int i = 0; i < 2; i++ ) { EyeRenderPoses[i] = Transformf(); } DistortionTimeCount = 0; VsyncEnabled = false; } void TimewarpMachine::Reset(HmdRenderInfo& renderInfo, bool vsyncEnabled, double timeNow) { RenderInfo = renderInfo; VsyncEnabled = vsyncEnabled; CurrentPredictionValues = PredictionGetDeviceValues ( renderInfo, true, VsyncEnabled ); PresentFlushToPresentFlushSeconds = 0.0f; DistortionTimeCount = 0; DistortionTimeAverage = 0.0f; LastFramePresentFlushTime = timeNow; AfterPresentAndFlush(timeNow); } void TimewarpMachine::AfterPresentAndFlush(double timeNow) { PresentFlushToPresentFlushSeconds = (float)(timeNow - LastFramePresentFlushTime); LastFramePresentFlushTime = timeNow; NextFramePresentFlushTime = timeNow + (double)PresentFlushToPresentFlushSeconds; } double TimewarpMachine::GetViewRenderPredictionTime() { // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us. return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToRenderedScene; } Transformf TimewarpMachine::GetViewRenderPredictionPose(SensorFusion &sfusion) { double predictionTime = GetViewRenderPredictionTime(); return sfusion.GetPoseAtTime(predictionTime); } double TimewarpMachine::GetVisiblePixelTimeStart() { // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us. return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToTimewarpStart; } double TimewarpMachine::GetVisiblePixelTimeEnd() { // Note that PredictionGetDeviceValues() did all the vsync-dependent thinking for us. return NextFramePresentFlushTime + CurrentPredictionValues.PresentFlushToTimewarpEnd; } Transformf TimewarpMachine::GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion) { double predictionTime = GetVisiblePixelTimeStart(); return sfusion.GetPoseAtTime(predictionTime); } Transformf TimewarpMachine::GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion) { double predictionTime = GetVisiblePixelTimeEnd(); return sfusion.GetPoseAtTime(predictionTime); } Matrix4f TimewarpMachine::GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose) { Transformf visiblePose = GetPredictedVisiblePixelPoseStart ( sfusion ); Matrix4f visibleMatrix(visiblePose); Matrix4f renderedMatrix(renderedPose); Matrix4f identity; // doesn't matter for orientation-only timewarp return TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity ); } Matrix4f TimewarpMachine::GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose) { Transformf visiblePose = GetPredictedVisiblePixelPoseEnd ( sfusion ); Matrix4f visibleMatrix(visiblePose); Matrix4f renderedMatrix(renderedPose); Matrix4f identity; // doesn't matter for orientation-only timewarp return TimewarpComputePoseDelta ( renderedMatrix, visibleMatrix, identity ); } // What time should the app wait until before starting distortion? double TimewarpMachine::JustInTime_GetDistortionWaitUntilTime() { if ( !VsyncEnabled || ( DistortionTimeCount < NumDistortionTimes ) ) { // Don't wait. return LastFramePresentFlushTime; } const float fudgeFactor = 0.002f; // Found heuristically - 1ms is too short because of timing granularity - may need further tweaking! float howLongBeforePresent = DistortionTimeAverage + fudgeFactor; // Subtlety here. Technically, the correct time is NextFramePresentFlushTime - howLongBeforePresent. // However, if the app drops a frame, this then perpetuates it, // i.e. if the display is running at 60fps, but the last frame was slow, // (e.g. because of swapping or whatever), then NextFramePresentFlushTime is // 33ms in the future, not 16ms. Since this function supplies the // time to wait until, the app will indeed wait until 32ms, so the framerate // drops to 30fps and never comes back up! // So we return the *ideal* framerate, not the *actual* framerate. return LastFramePresentFlushTime + (float)( CurrentPredictionValues.PresentFlushToPresentFlush - howLongBeforePresent ); } bool TimewarpMachine::JustInTime_NeedDistortionTimeMeasurement() const { if (!VsyncEnabled) { return false; } return ( DistortionTimeCount < NumDistortionTimes ); } void TimewarpMachine::JustInTime_BeforeDistortionTimeMeasurement(double timeNow) { DistortionTimeCurrentStart = timeNow; } void TimewarpMachine::JustInTime_AfterDistortionTimeMeasurement(double timeNow) { float timeDelta = (float)( timeNow - DistortionTimeCurrentStart ); if ( DistortionTimeCount < NumDistortionTimes ) { DistortionTimes[DistortionTimeCount] = timeDelta; DistortionTimeCount++; if ( DistortionTimeCount == NumDistortionTimes ) { // Median. float distortionTimeMedian = 0.0f; for ( int i = 0; i < NumDistortionTimes/2; i++ ) { // Find the maximum time of those remaining. float maxTime = DistortionTimes[0]; int maxIndex = 0; for ( int j = 1; j < NumDistortionTimes; j++ ) { if ( maxTime < DistortionTimes[j] ) { maxTime = DistortionTimes[j]; maxIndex = j; } } // Zero that max time, so we'll find the next-highest time. DistortionTimes[maxIndex] = 0.0f; distortionTimeMedian = maxTime; } DistortionTimeAverage = distortionTimeMedian; } } else { OVR_ASSERT ( !"Really didn't need more measurements, thanks" ); } } }}} // OVR::Util::Render \ No newline at end of file diff --git a/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Render_Stereo.h b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Render_Stereo.h new file mode 100644 index 0000000..a8bb9a7 --- /dev/null +++ b/modules/oculus_sdk_mac/LibOVR/Src/Util/Util_Render_Stereo.h @@ -0,0 +1 @@ +/************************************************************************************ PublicHeader: OVR.h Filename : Util_Render_Stereo.h Content : Sample stereo rendering configuration classes. Created : October 22, 2012 Authors : Michael Antonov, Tom Forsyth Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. Licensed under the Oculus VR Rift SDK License Version 3.1 (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.1 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. *************************************************************************************/ #ifndef OVR_Util_Render_Stereo_h #define OVR_Util_Render_Stereo_h #include "../OVR_Stereo.h" namespace OVR { class SensorFusion; namespace Util { namespace Render { //----------------------------------------------------------------------------------- // **** Useful debug functions. // // Purely for debugging - the results are not very end-user-friendly. char const* GetDebugNameEyeCupType ( EyeCupType eyeCupType ); char const* GetDebugNameHmdType ( HmdTypeEnum hmdType ); //----------------------------------------------------------------------------------- // **** Higher-level utility functions. Sizei CalculateRecommendedTextureSize ( HmdRenderInfo const &hmd, bool bRendertargetSharedByBothEyes, float pixelDensityInCenter = 1.0f ); FovPort CalculateRecommendedFov ( HmdRenderInfo const &hmd, StereoEye eyeType, bool bMakeFovSymmetrical = false); StereoEyeParams CalculateStereoEyeParams ( HmdRenderInfo const &hmd, StereoEye eyeType, Sizei const &actualRendertargetSurfaceSize, bool bRendertargetSharedByBothEyes, bool bRightHanded = true, float zNear = 0.01f, float zFar = 10000.0f, Sizei const *pOverrideRenderedPixelSize = NULL, FovPort const *pOverrideFovport = NULL, float zoomFactor = 1.0f ); Vector3f CalculateEyeVirtualCameraOffset(HmdRenderInfo const &hmd, StereoEye eyeType, bool bMonoRenderingMode ); // These are two components from StereoEyeParams that can be changed // very easily without full recomputation of everything. struct ViewportScaleAndOffset { Recti RenderedViewport; ScaleAndOffset2D EyeToSourceUV; }; // Three ways to override the size of the render view dynamically. // None of these require changing the distortion parameters or the regenerating the distortion mesh, // and can be called every frame if desired. ViewportScaleAndOffset ModifyRenderViewport ( StereoEyeParams const ¶ms, Sizei const &actualRendertargetSurfaceSize, Recti const &renderViewport ); ViewportScaleAndOffset ModifyRenderSize ( StereoEyeParams const ¶ms, Sizei const &actualRendertargetSurfaceSize, Sizei const &requestedRenderSize, bool bRendertargetSharedByBothEyes = false ); ViewportScaleAndOffset ModifyRenderDensity ( StereoEyeParams const ¶ms, Sizei const &actualRendertargetSurfaceSize, float pixelDensity = 1.0f, bool bRendertargetSharedByBothEyes = false ); //----------------------------------------------------------------------------------- // ***** StereoConfig // StereoConfig maintains a scene stereo state and allow switching between different // stereo rendering modes. To support rendering, StereoConfig keeps track of HMD // variables such as screen size, eye-to-screen distance and distortion, and computes // extra data such as FOV and distortion center offsets based on it. Rendering // parameters are returned though StereoEyeParams for each eye. // // Beyond regular 3D projection, this class supports rendering a 2D orthographic // surface for UI and text. The 2D surface will be defined by CreateOrthoSubProjection(). // The (0,0) coordinate corresponds to eye center location. // // Applications are not required to use this class, but they should be doing very // similar sequences of operations, and it may be useful to start with this class // and modify it. struct StereoEyeParamsWithOrtho { StereoEyeParams StereoEye; Matrix4f OrthoProjection; }; struct ViewportScaleAndOffsetBothEyes { ViewportScaleAndOffset Left; ViewportScaleAndOffset Right; }; class StereoConfig { public: // StereoMode describes rendering modes that can be used by StereoConfig. // These modes control whether stereo rendering is used or not (Stereo_None), // and how it is implemented. enum StereoMode { Stereo_None = 0, // Single eye Stereo_LeftRight_Multipass = 1, // One frustum per eye }; StereoConfig(StereoMode mode = Stereo_LeftRight_Multipass); //--------------------------------------------------------------------------------------------- // *** Core functions - every app MUST call these functions at least once. // Sets HMD parameters; also initializes distortion coefficients. void SetHmdRenderInfo(const HmdRenderInfo& hmd); // Set the physical size of the rendertarget surface the app created, // and whether one RT is shared by both eyes, or each eye has its own RT: // true: both eyes are rendered to the same RT. Left eye starts at top-left, right eye starts at top-middle. // false: each eye is rendered to its own RT. Some GPU architectures prefer this arrangement. // Typically, the app would call CalculateRecommendedTextureSize() to suggest the choice of RT size. // This setting must be exactly the size of the actual RT created, or the UVs produced will be incorrect. // If the app wants to render to a subsection of the RT, it should use SetRenderSize() void SetRendertargetSize (Size const rendertargetSize, bool rendertargetIsSharedByBothEyes ); // Returns full set of Stereo rendering parameters for the specified eye. const StereoEyeParamsWithOrtho& GetEyeRenderParams(StereoEye eye); //--------------------------------------------------------------------------------------------- // *** Optional functions - an app may call these to override default behaviours. const HmdRenderInfo& GetHmdRenderInfo() const { return Hmd; } // Returns the recommended size of rendertargets. // If rendertargetIsSharedByBothEyes is true, this is the size of the combined buffer. // If rendertargetIsSharedByBothEyes is false, this is the size of each individual buffer. // pixelDensityInCenter may be set to any number - by default it will match the HMD resolution in the center of the image. // After creating the rendertargets, the application MUST call SetRendertargetSize() with the actual size created // (which can be larger or smaller as the app wishes, but StereoConfig needs to know either way) Sizei CalculateRecommendedTextureSize ( bool rendertargetSharedByBothEyes, float pixelDensityInCenter = 1.0f ); // Sets a stereo rendering mode and updates internal cached // state (matrices, per-eye view) based on it. void SetStereoMode(StereoMode mode) { Mode = mode; DirtyFlag = true; } StereoMode GetStereoMode() const { return Mode; } // Sets the fieldOfView that the 2D coordinate area stretches to. void Set2DAreaFov(float fovRadians); // Really only for science experiments - no normal app should ever need to override // the HMD's lens descriptors. Passing NULL removes the override. // Supply both = set left and right. // Supply just left = set both to the same. // Supply neither = remove override. void SetLensOverride ( LensConfig const *pLensOverrideLeft = NULL, LensConfig const *pLensOverrideRight = NULL ); // Override the rendered FOV in various ways. All angles in tangent units. // This is not clamped to the physical FOV of the display - you'll need to do that yourself! // Supply both = set left and right. // Supply just left = set both to the same. // Supply neither = remove override. void SetFov ( FovPort const *pfovLeft = NULL, FovPort const *pfovRight = NULL ); void SetFovPortRadians ( float horizontal, float vertical ) { FovPort fov = FovPort::CreateFromRadians(horizontal, vertical); SetFov( &fov, &fov ); } // This forces a "zero IPD" mode where there is just a single render with an FOV that // is the union of the two calculated FOVs. // The calculated render is for the left eye. Any size & FOV overrides for the right // eye will be ignored. // If you query the right eye's size, you will get the same render // size & position as the left eye - you should not actually do the render of course! // The distortion values will be different, because it goes to a different place on the framebuffer. // Note that if you do this, the rendertarget does not need to be twice the width of // the render size any more. void SetZeroVirtualIpdOverride ( bool enableOverride ); // Allows the app to specify near and far clip planes and the right/left-handedness of the projection matrix. void SetZClipPlanesAndHandedness ( float zNear = 0.01f, float zFar = 10000.0f, bool rightHandedProjection = true ); // Allows the app to specify how much extra eye rotation to allow when determining the visible FOV. void SetExtraEyeRotation ( float extraEyeRotationInRadians = 0.0f ); // The dirty flag is set by any of the above calls. Just handy for the app to know // if e.g. the distortion mesh needs regeneration. void SetDirty() { DirtyFlag = true; } bool IsDirty() { return DirtyFlag; } // An app never needs to call this - GetEyeRenderParams will call it internally if // the state is dirty. However apps can call this explicitly to control when and where // computation is performed (e.g. not inside critical loops) void UpdateComputedState(); // This returns the projection matrix with a "zoom". Does not modify any internal state. Matrix4f GetProjectionWithZoom ( StereoEye eye, float fovZoom ) const; //--------------------------------------------------------------------------------------------- // The SetRender* functions are special. // // They do not require a full recalculation of state, and they do not change anything but the // ViewportScaleAndOffset data for the eyes (which they return), and do not set the dirty flag! // This means they can be called without regenerating the distortion mesh, and thus // can happily be called every frame without causing performance problems. Dynamic rescaling // of the rendertarget can help keep framerate up in demanding VR applications. // See the documentation for more details on their use. // Specify a pixel density - how many rendered pixels per pixel in the physical display. ViewportScaleAndOffsetBothEyes SetRenderDensity ( float pixelsPerDisplayPixel ); // Supply the size directly. Will be clamped to the physical rendertarget size. ViewportScaleAndOffsetBothEyes SetRenderSize ( Sizei const &renderSizeLeft, Sizei const &renderSizeRight ); // Supply the viewport directly. This is not clamped to the physical rendertarget - careful now! ViewportScaleAndOffsetBothEyes SetRenderViewport ( Recti const &renderViewportLeft, Recti const &renderViewportRight ); private: // *** Modifiable State StereoMode Mode; HmdRenderInfo Hmd; float Area2DFov; // FOV range mapping to the 2D area. // Only one of these three overrides can be true! enum SetViewportModeEnum { SVPM_Density, SVPM_Size, SVPM_Viewport, } SetViewportMode; // ...and depending which it is, one of the following are used. float SetViewportPixelsPerDisplayPixel; Sizei SetViewportSize[2]; Recti SetViewport[2]; // Other overrides. bool OverrideLens; LensConfig LensOverrideLeft; LensConfig LensOverrideRight; Sizei RendertargetSize; bool OverrideTanHalfFov; FovPort FovOverrideLeft; FovPort FovOverrideRight; bool OverrideZeroIpd; float ZNear; float ZFar; float ExtraEyeRotationInRadians; bool IsRendertargetSharedByBothEyes; bool RightHandedProjection; bool DirtyFlag; // Set when any if the modifiable state changed. Does NOT get set by SetRender*() // Utility function. ViewportScaleAndOffsetBothEyes setupViewportScaleAndOffsets(); // *** Computed State public: // Small hack for the config tool. Normal code should never read EyeRenderParams directly - use GetEyeRenderParams() instead. // 0/1 = left/right main views. StereoEyeParamsWithOrtho EyeRenderParams[2]; }; //----------------------------------------------------------------------------------- // ***** Distortion Mesh Rendering // // Stores both texture UV coords, or tan(angle) values. // Use whichever set of data the specific distortion algorithm requires. // This struct *must* be binary compatible with CAPI ovrDistortionVertex. struct DistortionMeshVertexData { // [-1,+1],[-1,+1] over the entire framebuffer. Vector2f ScreenPosNDC; // [0.0-1.0] interpolation value for timewarping - see documentation for details. float TimewarpLerp; // [0.0-1.0] fade-to-black at the edges to reduce peripheral vision noise. float Shade; // The red, green, and blue vectors in tan(angle) space. // Scale and offset by the values in StereoEyeParams.EyeToSourceUV.Scale // and StereoParams.EyeToSourceUV.Offset to get to real texture UV coords. Vector2f TanEyeAnglesR; Vector2f TanEyeAnglesG; Vector2f TanEyeAnglesB; }; void DistortionMeshCreate ( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ); // Generate distortion mesh for a eye. This version requires less data then stereoParms, supporting // dynamic change in render target viewport. void DistortionMeshCreate( DistortionMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, bool rightEye, const HmdRenderInfo &hmdRenderInfo, const DistortionRenderDesc &distortion, const ScaleAndOffset2D &eyeToSourceNDC ); void DistortionMeshDestroy ( DistortionMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ); //----------------------------------------------------------------------------------- // ***** Heightmap Mesh Rendering // // Stores both texture UV coords, or tan(angle) values. // This struct *must* be binary compatible with CAPI ovrHeightmapVertex. struct HeightmapMeshVertexData { // [-1,+1],[-1,+1] over the entire framebuffer. Vector2f ScreenPosNDC; // [0.0-1.0] interpolation value for timewarping - see documentation for details. float TimewarpLerp; // The vectors in tan(angle) space. // Scale and offset by the values in StereoEyeParams.EyeToSourceUV.Scale // and StereoParams.EyeToSourceUV.Offset to get to real texture UV coords. Vector2f TanEyeAngles; }; void HeightmapMeshCreate ( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, const StereoEyeParams &stereoParams, const HmdRenderInfo &hmdRenderInfo ); // Generate heightmap mesh for a eye. This version requires less data then stereoParms, supporting // dynamic change in render target viewport. void HeightmapMeshCreate( HeightmapMeshVertexData **ppVertices, UInt16 **ppTriangleListIndices, int *pNumVertices, int *pNumTriangles, bool rightEye, const HmdRenderInfo &hmdRenderInfo, const ScaleAndOffset2D &eyeToSourceNDC ); void HeightmapMeshDestroy ( HeightmapMeshVertexData *pVertices, UInt16 *pTriangleMeshIndices ); //----------------------------------------------------------------------------------- // ***** Prediction and timewarp. // struct PredictionValues { // All values in seconds. // These are the times in seconds from a present+flush to the relevant display element. // The time is measured to the middle of that element's visibility window, // e.g. if the device is a full-persistence display, the element will be visible for // an entire frame, so the time measures to the middle of that period, i.e. half the frame time. float PresentFlushToRenderedScene; // To the overall rendered 3D scene being visible. float PresentFlushToTimewarpStart; // To when the first timewarped scanline will be visible. float PresentFlushToTimewarpEnd; // To when the last timewarped scanline will be visible. float PresentFlushToPresentFlush; // To the next present+flush, i.e. the ideal framerate. bool WithTimewarp; bool WithVsync; }; // Calculates the values from the HMD info. PredictionValues PredictionGetDeviceValues ( const HmdRenderInfo &hmdRenderInfo, bool withTimewarp = true, bool withVsync = true ); // Pass in an orientation used to render the scene, and then the predicted orientation // (which may have been computed later on, and thus is more accurate), and this // will return the matrix to pass to the timewarp distortion shader. // TODO: deal with different handedness? Matrix4f TimewarpComputePoseDelta ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust ); Matrix4f TimewarpComputePoseDeltaPosition ( Matrix4f const &renderedViewFromWorld, Matrix4f const &predictedViewFromWorld, Matrix4f const&eyeViewAdjust ); // TimewarpMachine helps keep track of rendered frame timing and // handles predictions for time-warp rendering. class TimewarpMachine { public: TimewarpMachine(); // Call this on and every time something about the setup changes. void Reset ( HmdRenderInfo& renderInfo, bool vsyncEnabled, double timeNow ); // The only reliable time in most engines is directly after the frame-present and GPU flush-and-wait. // This call should be done right after that to give this system the timing info it needs. void AfterPresentAndFlush(double timeNow); // The "average" time the rendered frame will show up, // and the predicted pose of the HMD at that time. // You usually only need to call one of these functions. double GetViewRenderPredictionTime(); Transformf GetViewRenderPredictionPose(SensorFusion &sfusion); // Timewarp prediction functions. You usually only need to call one of these three sets of functions. // The predicted times that the first and last pixel will be visible on-screen. double GetVisiblePixelTimeStart(); double GetVisiblePixelTimeEnd(); // Predicted poses of the HMD at those first and last pixels. Transformf GetPredictedVisiblePixelPoseStart(SensorFusion &sfusion); Transformf GetPredictedVisiblePixelPoseEnd (SensorFusion &sfusion); // The delta matrices to feed to the timewarp distortion code, // given the pose that was used for rendering. // (usually the one returned by GetViewRenderPredictionPose() earlier) Matrix4f GetTimewarpDeltaStart(SensorFusion &sfusion, Transformf const &renderedPose); Matrix4f GetTimewarpDeltaEnd (SensorFusion &sfusion, Transformf const &renderedPose); // Just-In-Time distortion aims to delay the second sensor reading & distortion // until the very last moment to improve prediction. However, it is a little scary, // since the delay might wait too long and miss the vsync completely! // Use of the JustInTime_* functions is entirely optional, and we advise allowing // users to turn it off in their video options to cope with odd machine configurations. // What time should the app wait until before starting distortion? double JustInTime_GetDistortionWaitUntilTime(); // Used to time the distortion rendering bool JustInTime_NeedDistortionTimeMeasurement() const; void JustInTime_BeforeDistortionTimeMeasurement(double timeNow); void JustInTime_AfterDistortionTimeMeasurement(double timeNow); private: bool VsyncEnabled; HmdRenderInfo RenderInfo; PredictionValues CurrentPredictionValues; enum { NumDistortionTimes = 10 }; int DistortionTimeCount; double DistortionTimeCurrentStart; float DistortionTimes[NumDistortionTimes]; float DistortionTimeAverage; // Pose at which last time the eye was rendered. Transformf EyeRenderPoses[2]; // Absolute time of the last present+flush double LastFramePresentFlushTime; // Seconds between presentflushes float PresentFlushToPresentFlushSeconds; // Predicted absolute time of the next present+flush double NextFramePresentFlushTime; }; }}} // OVR::Util::Render #endif \ No newline at end of file diff --git a/modules/oculus_sdk_mac/readme.txt b/modules/oculus_sdk_mac/readme.txt new file mode 100644 index 0000000..92f3c1e --- /dev/null +++ b/modules/oculus_sdk_mac/readme.txt @@ -0,0 +1 @@ +Oculus SDK (C) Oculus VR, Inc. 2014. All Rights Reserved. The latest version of the Oculus SDK is available at http://developer.oculusvr.com. Please see the Oculus SDK Overview in the OculusSDK/Doc/ directory for more information. \ No newline at end of file diff --git a/src/oculus-vr/lib.rs b/src/oculus-vr/lib.rs index f32eaf8..a9468ba 100644 --- a/src/oculus-vr/lib.rs +++ b/src/oculus-vr/lib.rs @@ -15,7 +15,7 @@ use cgmath::vector::{Vector2, Vector3}; use cgmath::matrix::{Matrix4}; #[cfg(target_os = "linux")] -#[link(name="OVR_C")] +#[link(name="ovr")] #[link(name="stdc++")] #[link(name="udev")] #[link(name="Xinerama")] @@ -25,7 +25,7 @@ use cgmath::matrix::{Matrix4}; extern {} #[cfg(target_os = "macos")] -#[link(name="OVR_C")] +#[link(name="ovr")] #[link(name="stdc++")] #[link(name = "Cocoa", kind = "framework")] #[link(name = "IOKit", kind = "framework")]