pax_global_header00006660000000000000000000000064127720466420014524gustar00rootroot0000000000000052 comment=5d65570998bf4e2d3b7c8a273068b8ca65f9a502 OpenHMD-0.2.0/000077500000000000000000000000001277204664200127555ustar00rootroot00000000000000OpenHMD-0.2.0/.gitignore000066400000000000000000000004701277204664200147460ustar00rootroot00000000000000tests/simple/hmdtest tests/opengl/hmd-opengl tests/unittests/unittests *.exe *.dll *.o *.lo *.a *.la *.libs *.deps *~ .cproject .project build/ # Files generated by autoconf Makefile.in aclocal.m4 ar-lib autom4te.cache/ compile config.guess config.h.in config.sub configure depcomp install-sh ltmain.sh missing OpenHMD-0.2.0/.travis.yml000066400000000000000000000004641277204664200150720ustar00rootroot00000000000000# This is the project file for the automated build system travis (travis-ci.com). sudo: required dist: trusty language: c compiler: gcc before_install: - sudo apt-get update -qq - sudo apt-get install -qq libhidapi-dev - ./autogen.sh install: true # skip install step script: ./configure && make OpenHMD-0.2.0/CMakeLists.txt000066400000000000000000000035041277204664200155170ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8) project(openhmd C CXX) set(CMAKE_C_FLAGS "-std=c99") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") include_directories(${CMAKE_CURRENT_LIST_DIR}/include) if (MSVC) # Add the "lib" prefix for generated .lib outputs. set(LIB_PREFIX lib) else (MSVC) # When building with "make", "lib" prefix will be added automatically by # the build tool. set(LIB_PREFIX) endif (MSVC) #source files set just for Android set(openhmd_source_files ${CMAKE_CURRENT_LIST_DIR}/src/openhmd.c ${CMAKE_CURRENT_LIST_DIR}/src/platform-win32.c ${CMAKE_CURRENT_LIST_DIR}/src/drv_dummy/dummy.c ${CMAKE_CURRENT_LIST_DIR}/src/omath.c ${CMAKE_CURRENT_LIST_DIR}/src/platform-posix.c ${CMAKE_CURRENT_LIST_DIR}/src/fusion.c ) OPTION(OPENHMD_DRIVER_OCULUS_RIFT "Oculus Rift DK1 and DK2" ON) OPTION(OPENHMD_DRIVER_EXTERNAL "External sensor driver" ON) OPTION(OPENHMD_DRIVER_ANDROID "General Android driver" OFF) if(OPENHMD_DRIVER_OCULUS_RIFT) set(openhmd_source_files ${openhmd_source_files} ${CMAKE_CURRENT_LIST_DIR}/src/drv_oculus_rift/rift.c ${CMAKE_CURRENT_LIST_DIR}/src/drv_oculus_rift/packet.c ) add_definitions(-DDRIVER_OCULUS_RIFT) find_package(HIDAPI REQUIRED) include_directories(${HIDAPI_INCLUDE_DIRS}) set(LIBS ${LIBS} ${HIDAPI_LIBRARIES}) endif(OPENHMD_DRIVER_OCULUS_RIFT) if (OPENHMD_DRIVER_EXTERNAL) set(openhmd_source_files ${openhmd_source_files} ${CMAKE_CURRENT_LIST_DIR}/src/drv_external/external.c ) add_definitions(-DDRIVER_EXTERNAL) endif(OPENHMD_DRIVER_EXTERNAL) if (OPENHMD_DRIVER_ANDROID) set(openhmd_source_files ${openhmd_source_files} ${CMAKE_CURRENT_LIST_DIR}/src/drv_android/android.c ) add_definitions(-DDRIVER_ANDROID) endif(OPENHMD_DRIVER_ANDROID) add_library(openhmd ${openhmd_source_files} ${LIBS}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_DIR}/) OpenHMD-0.2.0/Doxyfile000066400000000000000000000003321277204664200144610ustar00rootroot00000000000000PROJECT_NAME = "OpenHMD" OPTIMIZE_OUTPUT_FOR_C = YES INPUT = include/ STRIP_CODE_COMMENTS = NO ALPHABETICAL_INDEX = NO IGNORE_PREFIX = ohmd_ OHMD_ ENUM_VALUES_PER_LINE = 1 OpenHMD-0.2.0/LICENSE000066400000000000000000000024721277204664200137670ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. OpenHMD-0.2.0/Makefile.am000066400000000000000000000002321277204664200150060ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign SUBDIRS = src tests examples pkgconfigdir = $(libdir)/$(PKG_CONFIG_EXTRA_PATH)pkgconfig pkgconfig_DATA = pkg-config/openhmd.pc OpenHMD-0.2.0/README.md000066400000000000000000000073271277204664200142450ustar00rootroot00000000000000# OpenHMD This project aims to provide a Free and Open Source API and drivers for immersive technology, such as head mounted displays with built in head tracking. ## License OpenHMD is released under the permissive Boost Software License (see LICENSE for more information), to make sure it can be linked and distributed with both free and non-free software. While it doesn't require contribution from the users, it is still very appreciated. ## Supported Devices * Oculus Rift DK1 and DK2 (rotation only) * Android based devices * External Sensor (passthrough for external sensors) ## Supported Platforms * Linux * Windows * OS X * Android * FreeBSD ## Requirements * Option 1: GNU Autotools (if you're building from the git repository) * Option 2: CMake * HIDAPI * http://www.signal11.us/oss/hidapi/ * https://github.com/signal11/hidapi/ ## Language Bindings * Java bindings by Joey Ferwerda and Koen Mertens * https://github.com/OpenHMD/OpenHMD-Java * .NET bindings by Jurrien Fakkeldij * https://github.com/jurrien-fakkeldij/OpenHMD.NET * Perl bindings by CandyAngel * https://github.com/CandyAngel/perl-openhmd * Python bindings by Lubosz Sarnecki * https://github.com/lubosz/python-rift ## Other FOSS HMD Drivers * libvr - http://hg.sitedethib.com/libvr ## Compiling and Installing Using make: ./autogen.sh # (if you're building from the git repository) ./configure [--enable-openglexample] make sudo make install Using CMake: With CMake, you can enable and disable drivers to compile OpenHMD with. Current Available drivers are: OPENHMD_DRIVER_OCULUS_RIFT, OPENHMD_DRIVER_EXTERNAL and OPENHMD_DRIVER_ANDROID. These can be enabled or disabled adding -DDRIVER_OF_CHOICE=ON after the cmake command (or using cmake-gui). cmake . make ### Configuring udev on Linux To avoid having to run your applications as root to access USB devices you have to add a udev rule (this will be included in .deb packages, etc). as root, run: echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="2833", MODE="0666", GROUP="plugdev"' > /etc/udev/rules.d/83-hmd.rules udevadm control --reload-rules After this you have to unplug your Rift and plug it back in. You should now be able to access the Oculus Rift as a normal user. ### Cross compiling for windows using mingw Using Make: export PREFIX=/usr/i686-w64-mingw32/ (or whatever your mingw path is) PKG_CONFIG_LIBDIR=$PREFIX/lib/pkgconfig ./configure --build=`gcc -dumpmachine` --host=i686-w64-mingw32 --prefix=$PREFIX make the library will end up in the .lib directory, you can use microsoft's lib.exe to make a .lib file for it Using CMake: For MinGW cross compiling, toolchain files tend to be the best solution. Please check the CMake documentation on how to do this. A starting point might be the CMake wiki: http://www.vtk.org/Wiki/CmakeMingw ### Static linking on windows If you're linking statically with OpenHMD using windows/mingw you have to make sure the macro OHMD_STATIC is set before including openhmd.h. In GCC this can be done by adding the compiler flag -DOHMD_STATIC, and with msvc it can be done using /DOHMD_STATIC. Note that this is *only* if you're linking statically! If you're using the DLL then you *must not* define OHMD_STATIC. (If you're not sure then you're probably linking dynamically and won't have to worry about this). ## Pre-built packages Will be available soon. ## Using OpenHMD See the examples/ subdirectory for usage examples. The OpenGL example is not built by default, to build it use the --enable-openglexample option for the configure script. It requires SDL, glew and OpenGL. An API reference can be generated using doxygen and is also available here: http://openhmd.net/doxygen/0.1.0/openhmd_8h.html OpenHMD-0.2.0/autogen.sh000077500000000000000000000000471277204664200147570ustar00rootroot00000000000000#!/bin/sh autoreconf --install --force OpenHMD-0.2.0/cmake/000077500000000000000000000000001277204664200140355ustar00rootroot00000000000000OpenHMD-0.2.0/cmake/FindHIDAPI.cmake000066400000000000000000000022741277204664200166030ustar00rootroot00000000000000# - try to find HIDAPI library # from http://www.signal11.us/oss/hidapi/ # # Cache Variables: (probably not for direct use in your scripts) # HIDAPI_INCLUDE_DIR # HIDAPI_LIBRARY # # Non-cache variables you might use in your CMakeLists.txt: # HIDAPI_FOUND # HIDAPI_INCLUDE_DIRS # HIDAPI_LIBRARIES # # Requires these CMake modules: # FindPackageHandleStandardArgs (known included with CMake >=2.6.2) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) find_library(HIDAPI_LIBRARY NAMES hidapi hidapi-libusb) find_path(HIDAPI_INCLUDE_DIR NAMES hidapi.h PATH_SUFFIXES hidapi) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(HIDAPI DEFAULT_MSG HIDAPI_LIBRARY HIDAPI_INCLUDE_DIR) if(HIDAPI_FOUND) set(HIDAPI_LIBRARIES "${HIDAPI_LIBRARY}") set(HIDAPI_INCLUDE_DIRS "${HIDAPI_INCLUDE_DIR}") endif() mark_as_advanced(HIDAPI_INCLUDE_DIR HIDAPI_LIBRARY) OpenHMD-0.2.0/configure.ac000066400000000000000000000063301277204664200152450ustar00rootroot00000000000000name='openhmd' version='0.0.1' library_interface_version='0:0:0' email='noname@nurd.se' AC_PREREQ([2.13]) AC_INIT([openhmd], [0.0.1], [noname@nurd.se]) AM_INIT_AUTOMAKE([foreign -Wall]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) LT_INIT AC_CANONICAL_HOST # 0.24 automatically calls AC_SUBST() in PKG_CHECK_MODULES() PKG_PROG_PKG_CONFIG([0.24]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) hidapi="hidapi" AC_SUBST(PKG_CONFIG_EXTRA_PATH, "") AC_SUBST(EXTRA_LD_FLAGS, "") AC_MSG_CHECKING([operating system]) AS_CASE(["$host"], [*-linux*], [AC_MSG_RESULT([$host (Linux)]) hidapi="hidapi-libusb" #link with realtime lib on linux for clock_gettime AC_SUBST(EXTRA_LD_FLAGS, "-lrt -lpthread")], [*-freebsd*], [AC_SUBST(PKG_CONFIG_EXTRA_PATH, "libdata/")]) # Oculus Rift Driver AC_ARG_ENABLE([driver-oculus-rift], [AS_HELP_STRING([--disable-driver-oculus-rift], [disable building of Oculus Rift driver [default=yes]])], [driver_oculus_rift_enabled=$enableval], [driver_oculus_rift_enabled='yes']) AM_CONDITIONAL([BUILD_DRIVER_OCULUS_RIFT], [test "x$driver_oculus_rift_enabled" != "xno"]) # External Driver AC_ARG_ENABLE([driver-external], [AS_HELP_STRING([--disable-driver-external], [disable building of External driver [default=yes]])], [driver_external_enabled=$enableval], [driver_external_enabled='yes']) AM_CONDITIONAL([BUILD_DRIVER_EXTERNAL], [test "x$driver_external_enabled" != "xno"]) # Android Driver AC_ARG_ENABLE([driver-android], [AS_HELP_STRING([--enable-driver-android], [enable building of Android driver [default=no]])], [driver_android_enabled=$enableval], [driver_android_enabled='no']) AM_CONDITIONAL([BUILD_DRIVER_ANDROID], [test "x$driver_android_enabled" != "xno"]) # Libs required by Oculus Rift Driver AS_IF([test "x$driver_oculus_rift_enabled" != "xno"], [PKG_CHECK_MODULES([hidapi], [$hidapi] >= 0.0.5)]) # Do we build OpenGL example? AC_ARG_ENABLE([openglexample], [AS_HELP_STRING([--enable-openglexample], [enable building of OpenGL example [default=no]])], [openglexample_enabled=$enableval], [openglexample_enabled='no']) AM_CONDITIONAL([BUILD_OPENGL_EXAMPLE], [test "x$openglexample_enabled" != "xno"]) # Libs required by OpenGL test AS_IF([test "x$openglexample_enabled" != "xno"], [ PKG_CHECK_MODULES([sdl], [sdl]) # Try to find OpenGL with pkg-config PKG_CHECK_MODULES([GL], [gl], [], # and try to find which lib to link to, -lGL first [AC_CHECK_LIB(GL, glBegin, [GL_LIBS=-lGL], # if that fails, try -lopengl32 (win32) [AC_CHECK_LIB(opengl32, main, [GL_LIBS=-lopengl32], AC_MSG_ERROR([GL not found]) )] )] ) AC_SUBST(GL_LIBS) # Try to find GLEW with pkg-config PKG_CHECK_MODULES([GLEW], [glew], [], # if that fails, check if there's a glew header [AC_CHECK_HEADER([GL/glew.h], [GLEW_LIBS=-lGLEW; GLEW_CFLAGS=-DGLEW_STATIC], AC_MSG_ERROR([GLEW not found]))] ) AC_SUBST(GLEW_LIBS) AC_SUBST(GLEW_CFLAGS) ]) AC_PROG_CC AC_PROG_CC_C99 AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile tests/unittests/Makefile examples/Makefile examples/opengl/Makefile examples/simple/Makefile]) AC_OUTPUT OpenHMD-0.2.0/examples/000077500000000000000000000000001277204664200145735ustar00rootroot00000000000000OpenHMD-0.2.0/examples/Makefile.am000066400000000000000000000001021277204664200166200ustar00rootroot00000000000000SUBDIRS = simple if BUILD_OPENGL_EXAMPLE SUBDIRS += opengl endif OpenHMD-0.2.0/examples/opengl/000077500000000000000000000000001277204664200160575ustar00rootroot00000000000000OpenHMD-0.2.0/examples/opengl/Makefile.am000066400000000000000000000004551277204664200201170ustar00rootroot00000000000000bin_PROGRAMS = openglexample AM_CPPFLAGS = -Wall -Werror -I$(top_srcdir)/include -DOHMD_STATIC $(sdl_CFLAGS) $(GLEW_CFLAGS) openglexample_SOURCES = gl.c main.c openglexample_LDADD = $(top_builddir)/src/libopenhmd.la -lm openglexample_LDFLAGS = -static-libtool-libs $(sdl_LIBS) $(GLEW_LIBS) $(GL_LIBS) OpenHMD-0.2.0/examples/opengl/gl.c000066400000000000000000000151171277204664200166320ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* OpenGL Test - GL Helper Functions Implementation */ #include "gl.h" #include #include #ifdef __unix #include #endif #ifndef M_PI #define M_PI 3.14159265359 #endif void init_gl(gl_ctx* ctx, int w, int h) { memset(ctx, 0, sizeof(gl_ctx)); // == Initialize SDL == int ret = SDL_Init(SDL_INIT_EVERYTHING); if(ret < 0){ printf("SDL_Init failed\n"); exit(-1); } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); ctx->screen = SDL_SetVideoMode(w, h, 0, SDL_OPENGL | SDL_GL_DOUBLEBUFFER); if(ctx->screen == NULL){ printf("SDL_SetVideoMode failed\n"); exit(-1); } // Disable ctrl-c catching on linux (and OS X?) #ifdef __unix signal(SIGINT, SIG_DFL); #endif // Load extensions. glewInit(); printf("OpenGL Renderer: %s\n", glGetString(GL_RENDERER)); printf("OpenGL Vendor: %s\n", glGetString(GL_VENDOR)); printf("OpenGL Version: %s\n", glGetString(GL_VERSION)); // == Initialize OpenGL == glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_ALPHA_TEST); glLoadIdentity(); glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glEnable(GL_POLYGON_SMOOTH); glLoadIdentity(); glViewport(0, 0, ctx->screen->w, ctx->screen->h); } void ortho(gl_ctx* ctx) { glMatrixMode(GL_PROJECTION); //glPushMatrix(); glLoadIdentity(); glOrtho(0.0f, ctx->screen->w, ctx->screen->h, 0.0f, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); //glPushMatrix(); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH); glDisable(GL_MULTISAMPLE); } void draw_cube() { glBegin(GL_QUADS); glVertex3f( 0.5f, 0.5f, -0.5f); /* Top Right Of The Quad (Top) */ glVertex3f( -0.5f, 0.5f, -0.5f); /* Top Left Of The Quad (Top) */ glVertex3f( -0.5f, 0.5f, 0.5f); /* Bottom Left Of The Quad (Top) */ glVertex3f( 0.5f, 0.5f, 0.5f); /* Bottom Right Of The Quad (Top) */ glVertex3f( 0.5f, -0.5f, 0.5f); /* Top Right Of The Quad (Botm) */ glVertex3f( -0.5f, -0.5f, 0.5f); /* Top Left Of The Quad (Botm) */ glVertex3f( -0.5f, -0.5f, -0.5f); /* Bottom Left Of The Quad (Botm) */ glVertex3f( 0.5f, -0.5f, -0.5f); /* Bottom Right Of The Quad (Botm) */ glVertex3f( 0.5f, 0.5f, 0.5f); /* Top Right Of The Quad (Front) */ glVertex3f( -0.5f, 0.5f, 0.5f); /* Top Left Of The Quad (Front) */ glVertex3f( -0.5f, -0.5f, 0.5f); /* Bottom Left Of The Quad (Front) */ glVertex3f( 0.5f, -0.5f, 0.5f); /* Bottom Right Of The Quad (Front) */ glVertex3f( 0.5f, -0.5f, -0.5f); /* Bottom Left Of The Quad (Back) */ glVertex3f( -0.5f, -0.5f, -0.5f); /* Bottom Right Of The Quad (Back) */ glVertex3f( -0.5f, 0.5f, -0.5f); /* Top Right Of The Quad (Back) */ glVertex3f( 0.5f, 0.5f, -0.5f); /* Top Left Of The Quad (Back) */ glVertex3f( -0.5f, 0.5f, 0.5f); /* Top Right Of The Quad (Left) */ glVertex3f( -0.5f, 0.5f, -0.5f); /* Top Left Of The Quad (Left) */ glVertex3f( -0.5f, -0.5f, -0.5f); /* Bottom Left Of The Quad (Left) */ glVertex3f( -0.5f, -0.5f, 0.5f); /* Bottom Right Of The Quad (Left) */ glVertex3f( 0.5f, 0.5f, -0.5f); /* Top Right Of The Quad (Right) */ glVertex3f( 0.5f, 0.5f, 0.5f); /* Top Left Of The Quad (Right) */ glVertex3f( 0.5f, -0.5f, 0.5f); /* Bottom Left Of The Quad (Right) */ glVertex3f( 0.5f, -0.5f, -0.5f); /* Bottom Right Of The Quad (Right) */ glEnd(); } static void compile_shader_src(GLuint shader, const char* src) { glShaderSource(shader, 1, &src, NULL); glCompileShader(shader); GLint status; GLint length; char log[4096] = {0}; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); glGetShaderInfoLog(shader, 4096, &length, log); if(status == GL_FALSE){ printf("compile failed %s\n", log); } } GLuint compile_shader(const char* vertex, const char* fragment) { // Create the handels GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); GLuint programShader = glCreateProgram(); // Attach the shaders to a program handel. glAttachShader(programShader, vertexShader); glAttachShader(programShader, fragmentShader); // Load and compile the Vertex Shader compile_shader_src(vertexShader, vertex); // Load and compile the Fragment Shader compile_shader_src(fragmentShader, fragment); // The shader objects are not needed any more, // the programShader is the complete shader to be used. glDeleteShader(vertexShader); glDeleteShader(fragmentShader); glLinkProgram(programShader); GLint status; GLint length; char log[4096] = {0}; glGetProgramiv(programShader, GL_LINK_STATUS, &status); glGetProgramInfoLog(programShader, 4096, &length, log); if(status == GL_FALSE){ printf("link failed %s\n", log); } return programShader; } void create_fbo(int eye_width, int eye_height, GLuint* fbo, GLuint* color_tex, GLuint* depth_tex) { glGenTextures(1, color_tex); glGenTextures(1, depth_tex); glGenFramebuffers(1, fbo); glBindTexture(GL_TEXTURE_2D, *color_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, eye_width, eye_height, 0, GL_RGBA, GL_UNSIGNED_INT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, *depth_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, eye_width, eye_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, *fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *color_tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, *depth_tex, 0); GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if(status != GL_FRAMEBUFFER_COMPLETE_EXT){ printf("failed to create fbo %x\n", status); } glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } OpenHMD-0.2.0/examples/opengl/gl.h000066400000000000000000000013241277204664200166320ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* OpenGL Test - Interface For GL Helper Functions */ #ifndef GL_H #define GL_H #include #include #include typedef struct { int w, h; SDL_Surface* screen; } gl_ctx; void ortho(gl_ctx* ctx); void perspective(gl_ctx* ctx); void init_gl(gl_ctx* ctx, int w, int h); void draw_cube(); GLuint compile_shader(const char* vertex, const char* fragment); void create_fbo(int eye_width, int eye_height, GLuint* fbo, GLuint* color_tex, GLuint* depth_tex); #endif OpenHMD-0.2.0/examples/opengl/main.c000066400000000000000000000132771277204664200171610ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* OpenGL Test - Main Implementation */ #include #include #include #include #include "gl.h" #define TEST_WIDTH 1280 #define TEST_HEIGHT 800 #define EYE_WIDTH (TEST_WIDTH / 2 * 2) #define EYE_HEIGHT (TEST_HEIGHT * 2) char* read_file(const char* filename) { FILE* f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); long len = ftell(f); fseek(f, 0, SEEK_SET); char* buffer = calloc(1, len + 1); assert(buffer); size_t ret = fread(buffer, len, 1, f); assert(ret); fclose(f); return buffer; } float randf() { return (float)rand() / (float)RAND_MAX; } GLuint gen_cubes() { GLuint list = glGenLists(1); // Set the random seed. srand(42); glNewList(list, GL_COMPILE); for(float a = 0.0f; a < 360.0f; a += 20.0f){ glPushMatrix(); glRotatef(a, 0, 1, 0); glTranslatef(0, 0, -1); glScalef(0.2, 0.2, 0.2); glRotatef(randf() * 360, randf(), randf(), randf()); glColor4f(randf(), randf(), randf(), randf() * .5f + .5f); draw_cube(); glPopMatrix(); } // draw floor glColor4f(0, 1.0f, .25f, .25f); glTranslatef(0, -2.5f, 0); draw_cube(); glEndList(); return list; } void draw_scene(GLuint list) { // draw cubes glCallList(list); } int main(int argc, char** argv) { ohmd_context* ctx = ohmd_ctx_create(); int num_devices = ohmd_ctx_probe(ctx); if(num_devices < 0){ printf("failed to probe devices: %s\n", ohmd_ctx_get_error(ctx)); return 1; } ohmd_device_settings* settings = ohmd_device_settings_create(ctx); // If OHMD_IDS_AUTOMATIC_UPDATE is set to 0, ohmd_ctx_update() must be called at least 10 times per second. // It is enabled by default. int auto_update = 1; ohmd_device_settings_seti(settings, OHMD_IDS_AUTOMATIC_UPDATE, &auto_update); ohmd_device* hmd = ohmd_list_open_device_s(ctx, 0, settings); ohmd_device_settings_destroy(settings); if(!hmd){ printf("failed to open device: %s\n", ohmd_ctx_get_error(ctx)); return 1; } gl_ctx gl; init_gl(&gl, TEST_WIDTH, TEST_HEIGHT); SDL_ShowCursor(SDL_DISABLE); char* vertex = read_file("shaders/test1.vert.glsl"); char* fragment = read_file("shaders/test1.frag.glsl"); GLuint shader = compile_shader(vertex, fragment); glUseProgram(shader); glUniform1i(glGetUniformLocation(shader, "warpTexture"), 0); glUseProgram(0); GLuint list = gen_cubes(); GLuint left_color_tex = 0, left_depth_tex = 0, left_fbo = 0; create_fbo(EYE_WIDTH, EYE_HEIGHT, &left_fbo, &left_color_tex, &left_depth_tex); GLuint right_color_tex = 0, right_depth_tex = 0, right_fbo = 0; create_fbo(EYE_WIDTH, EYE_HEIGHT, &right_fbo, &right_color_tex, &right_depth_tex); bool done = false; while(!done){ ohmd_ctx_update(ctx); SDL_Event event; while(SDL_PollEvent(&event)){ if(event.type == SDL_KEYDOWN){ switch(event.key.keysym.sym){ case SDLK_ESCAPE: done = true; break; case SDLK_F1: SDL_WM_ToggleFullScreen(gl.screen); break; case SDLK_F2: { // reset rotation and position float zero[] = {0, 0, 0, 1}; ohmd_device_setf(hmd, OHMD_ROTATION_QUAT, zero); ohmd_device_setf(hmd, OHMD_POSITION_VECTOR, zero); } break; default: break; } } } // Common scene state glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); float matrix[16]; // set hmd rotation, for left eye. glMatrixMode(GL_PROJECTION); ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_PROJECTION_MATRIX, matrix); glLoadMatrixf(matrix); glMatrixMode(GL_MODELVIEW); ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX, matrix); glLoadMatrixf(matrix); // Draw scene into framebuffer. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, left_fbo); glViewport(0, 0, EYE_WIDTH, EYE_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw_scene(list); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // set hmd rotation, for right eye. glMatrixMode(GL_PROJECTION); ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, matrix); glLoadMatrixf(matrix); glMatrixMode(GL_MODELVIEW); ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX, matrix); glLoadMatrixf(matrix); // Draw scene into framebuffer. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, right_fbo); glViewport(0, 0, EYE_WIDTH, EYE_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); draw_scene(list); // Clean up common draw state glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); // Setup ortho state. glUseProgram(shader); glViewport(0, 0, TEST_WIDTH, TEST_HEIGHT); glEnable(GL_TEXTURE_2D); glColor4d(1, 1, 1, 1); // Setup simple render state glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Draw left eye glBindTexture(GL_TEXTURE_2D, left_color_tex); glBegin(GL_QUADS); glTexCoord2d( 0, 0); glVertex3d( -1, -1, 0); glTexCoord2d( 1, 0); glVertex3d( 0, -1, 0); glTexCoord2d( 1, 1); glVertex3d( 0, 1, 0); glTexCoord2d( 0, 1); glVertex3d( -1, 1, 0); glEnd(); // Draw right eye glBindTexture(GL_TEXTURE_2D, right_color_tex); glBegin(GL_QUADS); glTexCoord2d( 0, 0); glVertex3d( 0, -1, 0); glTexCoord2d( 1, 0); glVertex3d( 1, -1, 0); glTexCoord2d( 1, 1); glVertex3d( 1, 1, 0); glTexCoord2d( 0, 1); glVertex3d( 0, 1, 0); glEnd(); // Clean up state. glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); glUseProgram(0); // Da swap-dawup! SDL_GL_SwapBuffers(); SDL_Delay(10); } ohmd_ctx_destroy(ctx); free(vertex); free(fragment); return 0; } OpenHMD-0.2.0/examples/opengl/shaders/000077500000000000000000000000001277204664200175105ustar00rootroot00000000000000OpenHMD-0.2.0/examples/opengl/shaders/test1.frag.glsl000066400000000000000000000025711277204664200223560ustar00rootroot00000000000000#version 120 // Taken from mts3d forums, from user fredrik. uniform sampler2D warpTexture; const vec2 LeftLensCenter = vec2(0.2863248, 0.5); const vec2 RightLensCenter = vec2(0.7136753, 0.5); const vec2 LeftScreenCenter = vec2(0.25, 0.5); const vec2 RightScreenCenter = vec2(0.75, 0.5); const vec2 Scale = vec2(0.1469278, 0.2350845); const vec2 ScaleIn = vec2(4, 2.5); const vec4 HmdWarpParam = vec4(1, 0.22, 0.24, 0); // Scales input texture coordinates for distortion. vec2 HmdWarp(vec2 in01, vec2 LensCenter) { vec2 theta = (in01 - LensCenter) * ScaleIn; // Scales to [-1, 1] float rSq = theta.x * theta.x + theta.y * theta.y; vec2 rvector = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq); return LensCenter + Scale * rvector; } void main() { // The following two variables need to be set per eye vec2 LensCenter = gl_FragCoord.x < 640 ? LeftLensCenter : RightLensCenter; vec2 ScreenCenter = gl_FragCoord.x < 640 ? LeftScreenCenter : RightScreenCenter; vec2 oTexCoord = gl_FragCoord.xy / vec2(1280, 800); vec2 tc = HmdWarp(oTexCoord, LensCenter); if (any(bvec2(clamp(tc,ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)) - tc))) { gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); return; } tc.x = gl_FragCoord.x < 640 ? (2.0 * tc.x) : (2.0 * (tc.x - 0.5)); gl_FragColor = texture2D(warpTexture, tc); } OpenHMD-0.2.0/examples/opengl/shaders/test1.vert.glsl000066400000000000000000000001451277204664200224120ustar00rootroot00000000000000#version 120 void main(void) { gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = ftransform(); } OpenHMD-0.2.0/examples/simple/000077500000000000000000000000001277204664200160645ustar00rootroot00000000000000OpenHMD-0.2.0/examples/simple/Makefile.am000066400000000000000000000003051277204664200201160ustar00rootroot00000000000000bin_PROGRAMS = simple AM_CPPFLAGS = -Wall -I$(top_srcdir)/include -DOHMD_STATIC simple_SOURCES = simple.c simple_LDADD = $(top_builddir)/src/libopenhmd.la -lm simple_LDFLAGS = -static-libtool-libs OpenHMD-0.2.0/examples/simple/simple.c000066400000000000000000000050761277204664200175310ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Simple Test */ #include #include void ohmd_sleep(double); // gets float values from the device and prints them void print_infof(ohmd_device* hmd, const char* name, int len, ohmd_float_value val) { float f[len]; ohmd_device_getf(hmd, val, f); printf("%-20s", name); for(int i = 0; i < len; i++) printf("%f ", f[i]); printf("\n"); } int main(int argc, char** argv) { ohmd_context* ctx = ohmd_ctx_create(); // Probe for devices int num_devices = ohmd_ctx_probe(ctx); if(num_devices < 0){ printf("failed to probe devices: %s\n", ohmd_ctx_get_error(ctx)); return -1; } printf("num devices: %d\n\n", num_devices); // Print device information for(int i = 0; i < num_devices; i++){ printf("device %d\n", i); printf(" vendor: %s\n", ohmd_list_gets(ctx, i, OHMD_VENDOR)); printf(" product: %s\n", ohmd_list_gets(ctx, i, OHMD_PRODUCT)); printf(" path: %s\n\n", ohmd_list_gets(ctx, i, OHMD_PATH)); } // Open default device (0) ohmd_device* hmd = ohmd_list_open_device(ctx, 0); if(!hmd){ printf("failed to open device: %s\n", ohmd_ctx_get_error(ctx)); return -1; } // Print hardware information for the opened device int ivals[2]; ohmd_device_geti(hmd, OHMD_SCREEN_HORIZONTAL_RESOLUTION, ivals); ohmd_device_geti(hmd, OHMD_SCREEN_VERTICAL_RESOLUTION, ivals + 1); printf("resolution: %i x %i\n", ivals[0], ivals[1]); print_infof(hmd, "hsize:", 1, OHMD_SCREEN_HORIZONTAL_SIZE); print_infof(hmd, "vsize:", 1, OHMD_SCREEN_VERTICAL_SIZE); print_infof(hmd, "lens separation:", 1, OHMD_LENS_HORIZONTAL_SEPARATION); print_infof(hmd, "lens vcenter:", 1, OHMD_LENS_VERTICAL_POSITION); print_infof(hmd, "left eye fov:", 1, OHMD_LEFT_EYE_FOV); print_infof(hmd, "right eye fov:", 1, OHMD_RIGHT_EYE_FOV); print_infof(hmd, "left eye aspect:", 1, OHMD_LEFT_EYE_ASPECT_RATIO); print_infof(hmd, "right eye aspect:", 1, OHMD_RIGHT_EYE_ASPECT_RATIO); print_infof(hmd, "distortion k:", 6, OHMD_DISTORTION_K); printf("\n"); // Ask for n rotation quaternions for(int i = 0; i < 10000; i++){ ohmd_ctx_update(ctx); float zero[] = {.0, .1, .2, 1}; ohmd_device_setf(hmd, OHMD_ROTATION_QUAT, zero); ohmd_device_setf(hmd, OHMD_POSITION_VECTOR, zero); print_infof(hmd, "rotation quat:", 4, OHMD_ROTATION_QUAT); ohmd_sleep(.01); } ohmd_ctx_destroy(ctx); return 0; } OpenHMD-0.2.0/include/000077500000000000000000000000001277204664200144005ustar00rootroot00000000000000OpenHMD-0.2.0/include/openhmd.h000066400000000000000000000322611277204664200162070ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /** * \file openhmd.h * Main header for OpenHMD public API. **/ #ifndef OPENHMD_H #define OPENHMD_H #ifdef __cplusplus extern "C" { #endif #ifdef _WIN32 #ifdef DLL_EXPORT #define OHMD_APIENTRY __cdecl #define OHMD_APIENTRYDLL __declspec( dllexport ) #else #ifdef OHMD_STATIC #define OHMD_APIENTRY __cdecl #define OHMD_APIENTRYDLL #else #define OHMD_APIENTRY __cdecl #define OHMD_APIENTRYDLL __declspec( dllimport ) #endif #endif #else #define OHMD_APIENTRY #define OHMD_APIENTRYDLL #endif /** Maximum length of a string, including termination, in OpenHMD. */ #define OHMD_STR_SIZE 256 /** Return status codes, used for all functions that can return an error. */ typedef enum { OHMD_S_OK = 0, OHMD_S_UNKNOWN_ERROR = -1, OHMD_S_INVALID_PARAMETER = -2, OHMD_S_UNSUPPORTED = -3, /** OHMD_S_USER_RESERVED and below can be used for user purposes, such as errors within ohmd wrappers, etc. */ OHMD_S_USER_RESERVED = -16384, } ohmd_status; /** A collection of string value information types, used for getting information with ohmd_list_gets(). */ typedef enum { OHMD_VENDOR = 0, OHMD_PRODUCT = 1, OHMD_PATH = 2, } ohmd_string_value; /** A collection of float value information types, used for getting and setting information with ohmd_device_getf() and ohmd_device_setf(). */ typedef enum { /** float[4] (get): Absolute rotation of the device, in space, as a quaternion (x, y, z, w). */ OHMD_ROTATION_QUAT = 1, /** float[16] (get): A "ready to use" OpenGL style 4x4 matrix with a modelview matrix for the left eye of the HMD. */ OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX = 2, /** float[16] (get): A "ready to use" OpenGL style 4x4 matrix with a modelview matrix for the right eye of the HMD. */ OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX = 3, /** float[16] (get): A "ready to use" OpenGL style 4x4 matrix with a projection matrix for the left eye of the HMD. */ OHMD_LEFT_EYE_GL_PROJECTION_MATRIX = 4, /** float[16] (get): A "ready to use" OpenGL style 4x4 matrix with a projection matrix for the right eye of the HMD. */ OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX = 5, /** float[3] (get): A 3-D vector representing the absolute position of the device, in space. */ OHMD_POSITION_VECTOR = 6, /** float[1] (get): Physical width of the device screen in metres. */ OHMD_SCREEN_HORIZONTAL_SIZE = 7, /** float[1] (get): Physical height of the device screen in metres. */ OHMD_SCREEN_VERTICAL_SIZE = 8, /** float[1] (get): Physical separation of the device lenses in metres. */ OHMD_LENS_HORIZONTAL_SEPARATION = 9, /** float[1] (get): Physical vertical position of the lenses in metres. */ OHMD_LENS_VERTICAL_POSITION = 10, /** float[1] (get): Physical field of view for the left eye in degrees. */ OHMD_LEFT_EYE_FOV = 11, /** float[1] (get): Physical display aspect ratio for the left eye screen. */ OHMD_LEFT_EYE_ASPECT_RATIO = 12, /** float[1] (get): Physical field of view for the left right in degrees. */ OHMD_RIGHT_EYE_FOV = 13, /** float[1] (get): Physical display aspect ratio for the right eye screen. */ OHMD_RIGHT_EYE_ASPECT_RATIO = 14, /** float[1] (get, set): Physical interpupillary distance of the user in metres. */ OHMD_EYE_IPD = 15, /** float[1] (get, set): Z-far value for the projection matrix calculations (i.e. drawing distance). */ OHMD_PROJECTION_ZFAR = 16, /** float[1] (get, set): Z-near value for the projection matrix calculations (i.e. close clipping distance). */ OHMD_PROJECTION_ZNEAR = 17, /** float[6] (get): Device specific distortion value. */ OHMD_DISTORTION_K = 18, /** * float[10] (set): Perform sensor fusion on values from external sensors. * * Values are: dt (time since last update in seconds) X, Y, Z gyro, X, Y, Z accelerometer and X, Y, Z magnetometer. **/ OHMD_EXTERNAL_SENSOR_FUSION = 19, } ohmd_float_value; /** A collection of int value information types used for getting information with ohmd_device_geti(). */ typedef enum { /** int[1] (get): Physical horizontal resolution of the device screen. */ OHMD_SCREEN_HORIZONTAL_RESOLUTION = 0, /** int[1] (get): Physical vertical resolution of the device screen. */ OHMD_SCREEN_VERTICAL_RESOLUTION = 1, } ohmd_int_value; /** A collection of data information types used for setting information with ohmd_set_data(). */ typedef enum { /** void* (set): Set void* data for use in the internal drivers. */ OHMD_DRIVER_DATA = 0, /** * ohmd_device_properties* (set): * Set the device properties based on the ohmd_device_properties struct for use in the internal drivers. * * This can be used to fill in information about the device internally, such as Android, or for setting profiles. **/ OHMD_DRIVER_PROPERTIES = 1, } ohmd_data_value; typedef enum { /** int[1] (set, default: 1): Set this to 0 to prevent OpenHMD from creating background threads to do automatic device ticking. Call ohmd_update(); must be called frequently, at least 10 times per second, if the background threads are disabled. */ OHMD_IDS_AUTOMATIC_UPDATE = 0, } ohmd_int_settings; /** An opaque pointer to a context structure. */ typedef struct ohmd_context ohmd_context; /** An opaque pointer to a structure representing a device, such as an HMD. */ typedef struct ohmd_device ohmd_device; /** An opaque pointer to a structure representing arguments for a device. */ typedef struct ohmd_device_settings ohmd_device_settings; /** * Create an OpenHMD context. * * @return a pointer to an allocated ohmd_context on success or NULL if it fails. **/ OHMD_APIENTRYDLL ohmd_context* OHMD_APIENTRY ohmd_ctx_create(void); /** * Destroy an OpenHMD context. * * ohmd_ctx_destroy de-initializes and de-allocates an OpenHMD context allocated with ohmd_ctx_create. * All devices associated with the context are automatically closed. * * @param ctx The context to destroy. **/ OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_destroy(ohmd_context* ctx); /** * Get the last error as a human readable string. * * If a function taking a context as an argument (ohmd_context "methods") returns non-successfully, * a human readable error message describing what went wrong can be retrieved with this function. * * @param ctx The context to retrieve the error message from. * @return a pointer to the error message. **/ OHMD_APIENTRYDLL const char* OHMD_APIENTRY ohmd_ctx_get_error(ohmd_context* ctx); /** * Update a context. * * Update the values for the devices handled by a context. * * If background threads are disabled, this performs tasks like pumping events from the device. The exact details * are up to the driver but try to call it quite frequently. * Once per frame in a "game loop" should be sufficient. * If OpenHMD is handled in a background thread in your program, calling ohmd_ctx_update and then sleeping for 10-20 ms * is recommended. * * @param ctx The context that needs updating. **/ OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_update(ohmd_context* ctx); /** * Probe for devices. * * Probes for and enumerates supported devices attached to the system. * * @param ctx A context with no currently open devices. * @return the number of devices found on the system. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_ctx_probe(ohmd_context* ctx); /** * Get device description from enumeration list index. * * Gets a human readable device description string from a zero indexed enumeration index * between 0 and (max - 1), where max is the number ohmd_ctx_probe returned * (i.e. if ohmd_ctx_probe returns 3, valid indices are 0, 1 and 2). * The function can return three types of data. The vendor name, the product name and * a driver specific path where the device is attached. * * ohmd_ctx_probe must be called before calling ohmd_list_gets. * * @param ctx A (probed) context. * @param index An index, between 0 and the value returned from ohmd_ctx_probe. * @param type The type of data to fetch. One of OHMD_VENDOR, OHMD_PRODUCT and OHMD_PATH. * @return a string with a human readable device name. **/ OHMD_APIENTRYDLL const char* OHMD_APIENTRY ohmd_list_gets(ohmd_context* ctx, int index, ohmd_string_value type); /** * Open a device. * * Opens a device from a zero indexed enumeration index between 0 and (max - 1) * where max is the number ohmd_ctx_probe returned (i.e. if ohmd_ctx_probe returns 3, * valid indices are 0, 1 and 2). * * ohmd_ctx_probe must be called before calling ohmd_list_open_device. * * @param ctx A (probed) context. * @param index An index, between 0 and the value returned from ohmd_ctx_probe. * @return a pointer to an ohmd_device, which represents a hardware device, such as an HMD. **/ OHMD_APIENTRYDLL ohmd_device* OHMD_APIENTRY ohmd_list_open_device(ohmd_context* ctx, int index); /** * Open a device with additional settings provided. * * Opens a device from a zero indexed enumeration index between 0 and (max - 1) * where max is the number ohmd_ctx_probe returned (i.e. if ohmd_ctx_probe returns 3, * valid indices are 0, 1 and 2). * * ohmd_ctx_probe must be called before calling ohmd_list_open_device. * * @param ctx A (probed) context. * @param index An index, between 0 and the value returned from ohmd_ctx_probe. * @param settings A pointer to a device settings struct. * @return a pointer to an ohmd_device, which represents a hardware device, such as an HMD. **/ OHMD_APIENTRYDLL ohmd_device* OHMD_APIENTRY ohmd_list_open_device_s(ohmd_context* ctx, int index, ohmd_device_settings* settings); /** * Specify int settings in a device settings struct. * * @param settings The device settings struct to set values to. * @param key The specefic setting you wish to set. * @param value A pointer to an int or int array (containing the expected number of elements) with the value(s) you wish to set. **/ OHMD_APIENTRYDLL ohmd_status OHMD_APIENTRY ohmd_device_settings_seti(ohmd_device_settings* settings, ohmd_int_settings key, const int* val); /** * Create a device settings instance. * * @param ctx A pointer to a valid ohmd_context. * @return a pointer to an allocated ohmd_context on success or NULL if it fails. **/ OHMD_APIENTRYDLL ohmd_device_settings* OHMD_APIENTRY ohmd_device_settings_create(ohmd_context* ctx); /** * Destroy a device settings instance. * * @param ctx The device settings instance to destroy. **/ OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_device_settings_destroy(ohmd_device_settings* settings); /** * Close a device. * * Closes a device opened by ohmd_list_open_device. Note that ohmd_ctx_destroy automatically closes any open devices * associated with the context being destroyed. * * @param device The open device. * @return 0 on success, <0 on failure. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_close_device(ohmd_device* device); /** * Get a floating point value from a device. * * * @param device An open device to retrieve the value from. * @param type What type of value to retrieve, see ohmd_float_value section for more information. * @param[out] out A pointer to a float, or float array where the retrieved value should be written. * @return 0 on success, <0 on failure. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_getf(ohmd_device* device, ohmd_float_value type, float* out); /** * Set a floating point value for a device. * * @param device An open device to set the value in. * @param type What type of value to set, see ohmd_float_value section for more information. * @param in A pointer to a float, or float array where the new value is stored. * @return 0 on success, <0 on failure. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_setf(ohmd_device* device, ohmd_float_value type, const float* in); /** * Get an integer value from a device. * * @param device An open device to retrieve the value from. * @param type What type of value to retrieve, ohmd_int_value section for more information. * @param[out] out A pointer to an integer, or integer array where the retrieved value should be written. * @return 0 on success, <0 on failure. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_geti(ohmd_device* device, ohmd_int_value type, int* out); /** * Set an integer value for a device. * * @param device An open device to set the value in. * @param type What type of value to set, see ohmd_float_value section for more information. * @param in A pointer to a int, or int array where the new value is stored. * @return 0 on success, <0 on failure. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_seti(ohmd_device* device, ohmd_int_value type, const int* in); /** * Set an void* data value for a device. * * @param device An open device to set the value in. * @param type What type of value to set, see ohmd_float_value section for more information. * @param in A pointer to the void* casted object. * @return 0 on success, <0 on failure. **/ OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_device_set_data(ohmd_device* device, ohmd_data_value type, const void* in); #ifdef __cplusplus } #endif #endif OpenHMD-0.2.0/pkg-config/000077500000000000000000000000001277204664200150015ustar00rootroot00000000000000OpenHMD-0.2.0/pkg-config/openhmd.pc000066400000000000000000000004241277204664200167570ustar00rootroot00000000000000prefix=${pcfiledir}/../.. libdir=${prefix}/lib includedir=${prefix}/include/openhmd Name: openhmd Description: API and drivers for immersive technology devices such as HMDs Version: 0.0.1 Requires: hidapi-libusb Conflicts: Libs: -L${libdir} -lopenhmd Cflags: -I${includedir} OpenHMD-0.2.0/src/000077500000000000000000000000001277204664200135445ustar00rootroot00000000000000OpenHMD-0.2.0/src/Makefile.am000066400000000000000000000015101277204664200155750ustar00rootroot00000000000000AUTOMAKE_OPTIONS = subdir-objects library_includedir=$(includedir)/openhmd library_include_HEADERS = $(top_srcdir)/include/openhmd.h lib_LTLIBRARIES = libopenhmd.la libopenhmd_la_SOURCES = \ openhmd.c \ platform-win32.c \ drv_dummy/dummy.c \ omath.c \ platform-posix.c \ fusion.c libopenhmd_la_LDFLAGS = -no-undefined -version-info 0:0:0 libopenhmd_la_CPPFLAGS = -fPIC -I$(top_srcdir)/include -Wall if BUILD_DRIVER_OCULUS_RIFT libopenhmd_la_SOURCES += \ drv_oculus_rift/rift.c \ drv_oculus_rift/packet.c libopenhmd_la_CPPFLAGS += $(hidapi_CFLAGS) -DDRIVER_OCULUS_RIFT libopenhmd_la_LDFLAGS += $(hidapi_LIBS) endif if BUILD_DRIVER_EXTERNAL libopenhmd_la_SOURCES += \ drv_external/external.c endif if BUILD_DRIVER_ANDROID libopenhmd_la_SOURCES += \ drv_android/android.c endif libopenhmd_la_LDFLAGS += $(EXTRA_LD_FLAGS) OpenHMD-0.2.0/src/drv_android/000077500000000000000000000000001277204664200160375ustar00rootroot00000000000000OpenHMD-0.2.0/src/drv_android/android.c000066400000000000000000000227011277204664200176250ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Copyright (C) 2015 Joey Ferwerda * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Android Driver */ #include "android.h" #ifdef __ANDROID__ #include #endif // __ANDROID__ typedef struct { ohmd_device base; fusion sensor_fusion; //Android specific #ifdef __ANDROID__ android_app* state; ASensorManager* sensorManager; const ASensor* accelerometerSensor; const ASensor* gyroscopeSensor; ASensorEventQueue* sensorEventQueue; AAssetManager* assetMgr; short firstRun; #endif } android_priv; //Forward decelerations static void set_android_properties(ohmd_device* device, ohmd_device_properties* props); static void nofusion_init(fusion* me); static void nofusion_update(fusion* me, float dt, const vec3f* accel); //Static variable for timeDelta; static float timestamp; //Android callback for the sensor event queue static int android_sensor_callback(int fd, int events, void* data) { android_priv* priv = (android_priv*)data; if (priv->accelerometerSensor != NULL) { ASensorEvent event; vec3f gyro; vec3f accel; vec3f mag; float lastevent_timestamp; while (ASensorEventQueue_getEvents(priv->sensorEventQueue, &event, 1) > 0) { if (event.type == ASENSOR_TYPE_ACCELEROMETER) { accel.x = event.acceleration.y; accel.y = -event.acceleration.x; accel.z = event.acceleration.z; } if (event.type == ASENSOR_TYPE_GYROSCOPE) { gyro.x = -event.data[1]; gyro.y = event.data[0]; gyro.z = event.data[2]; } ///TODO: Implement mag when available mag.x = 0.0f; mag.y = 0.0f; mag.z = 0.0f; lastevent_timestamp = event.timestamp; } //apply data to the fusion float dT = 0.0f; if (timestamp != 0) dT= (lastevent_timestamp - timestamp) * (1.0f / 1000000000.0f); //Check if accelerometer only fallback is required if (!priv->gyroscopeSensor) nofusion_update(&priv->sensor_fusion, dT, &accel); else ofusion_update(&priv->sensor_fusion, dT, &gyro, &accel, &mag); //default timestamp = lastevent_timestamp; } return 1; } static void update_device(ohmd_device* device) { android_priv* priv = (android_priv*)device; if(!priv->state) return; //We need this since during init the android_app state is not set yet if (priv->firstRun == 1) { priv->sensorEventQueue = ASensorManager_createEventQueue(priv->sensorManager, priv->state->looper, LOOPER_ID_USER, android_sensor_callback, (void*)priv); // Start sensors in case this was not done already. if (priv->accelerometerSensor != NULL) { ASensorEventQueue_enableSensor(priv->sensorEventQueue, priv->accelerometerSensor); // We'd like to get 60 events per second (in us). ASensorEventQueue_setEventRate(priv->sensorEventQueue, priv->accelerometerSensor, (1000L/60)*1000); } if (priv->gyroscopeSensor != NULL) { ASensorEventQueue_enableSensor(priv->sensorEventQueue, priv->gyroscopeSensor); // We'd like to get 60 events per second (in us). ASensorEventQueue_setEventRate(priv->sensorEventQueue, priv->gyroscopeSensor, (1000L/60)*1000); } priv->firstRun = 0; } } static int getf(ohmd_device* device, ohmd_float_value type, float* out) { android_priv* priv = (android_priv*)device; switch(type){ case OHMD_ROTATION_QUAT: { *(quatf*)out = priv->sensor_fusion.orient; break; } case OHMD_POSITION_VECTOR: out[0] = out[1] = out[2] = 0; break; case OHMD_DISTORTION_K: // TODO this should be set to the equivalent of no distortion memset(out, 0, sizeof(float) * 6); break; default: ohmd_set_error(priv->base.ctx, "invalid type given to getf (%d)", type); return -1; break; } return 0; } static int set_data(ohmd_device* device, ohmd_data_value type, void* in) { android_priv* priv = (android_priv*)device; switch(type){ case OHMD_DRIVER_DATA: { priv->state = (android_app*)in; break; } case OHMD_DRIVER_PROPERTIES: { set_android_properties(device, (ohmd_device_properties*)in); break; } default: ohmd_set_error(priv->base.ctx, "invalid type given to set_data (%i)", type); return -1; break; } return 0; } static void close_device(ohmd_device* device) { LOGD("closing Android device"); free(device); } static ohmd_device* open_device(ohmd_driver* driver, ohmd_device_desc* desc) { android_priv* priv = ohmd_alloc(driver->ctx, sizeof(android_priv)); if(!priv) return NULL; // Set default device properties ohmd_set_default_device_properties(&priv->base.properties); // Set device properties //TODO: Get information from android about device //TODO: Use profile string to set default for a particular device (Durovis, VR One etc) priv->base.properties.hsize = 0.149760f; priv->base.properties.vsize = 0.093600f; priv->base.properties.hres = 1280; priv->base.properties.vres = 800; priv->base.properties.lens_sep = 0.063500; priv->base.properties.lens_vpos = 0.046800; priv->base.properties.fov = DEG_TO_RAD(125.5144f); priv->base.properties.ratio = (1280.0f / 800.0f) / 2.0f; // calculate projection eye projection matrices from the device properties ohmd_calc_default_proj_matrices(&priv->base.properties); // set up device callbacks priv->base.update = update_device; priv->base.close = close_device; priv->base.getf = getf; priv->base.set_data = set_data; //init Android sensors priv->sensorManager = ASensorManager_getInstance(); priv->accelerometerSensor = ASensorManager_getDefaultSensor(priv->sensorManager, ASENSOR_TYPE_ACCELEROMETER); priv->gyroscopeSensor = ASensorManager_getDefaultSensor(priv->sensorManager, ASENSOR_TYPE_GYROSCOPE); priv->firstRun = 1; //need this since ASensorManager_createEventQueue requires a set android_app* //Check if accelerometer only fallback is required if (!priv->gyroscopeSensor) nofusion_init(&priv->sensor_fusion); else ofusion_init(&priv->sensor_fusion); //Default when all sensors are available return (ohmd_device*)priv; } static void get_device_list(ohmd_driver* driver, ohmd_device_list* list) { ohmd_device_desc* desc = &list->devices[list->num_devices++]; strcpy(desc->driver, "OpenHMD Generic Android Driver"); strcpy(desc->vendor, "OpenHMD"); strcpy(desc->product, "Android Device"); strcpy(desc->path, "(none)"); desc->driver_ptr = driver; } static void destroy_driver(ohmd_driver* drv) { LOGD("shutting down Android driver"); free(drv); } ohmd_driver* ohmd_create_android_drv(ohmd_context* ctx) { ohmd_driver* drv = ohmd_alloc(ctx, sizeof(ohmd_driver)); if(!drv) return NULL; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->destroy = destroy_driver; return drv; } /* Android specific functions */ static void nofusion_update(fusion* me, float dt, const vec3f* accel) { //avg raw accel data to smooth jitter, and normalise ofq_add(&me->accel_fq, accel); vec3f accel_mean; ofq_get_mean(&me->accel_fq, &accel_mean); vec3f acc_n = accel_mean; ovec3f_normalize_me(&acc_n); //reference vectors for axis-angle vec3f xyzv[3] = { {1,0,0}, {0,1,0}, {0,0,1} }; quatf roll, pitch; //pitch is rot around x, based on gravity in z and y axes oquatf_init_axis(&pitch, xyzv+0, atan2f(-acc_n.z, -acc_n.y)); //roll is rot around z, based on gravity in x and y axes //note we need to invert the values when the device is upside down (y < 0) for proper results oquatf_init_axis(&roll, xyzv+2, acc_n.y < 0 ? atan2f(-acc_n.x, -acc_n.y) : atan2f(acc_n.x, acc_n.y)); quatf or = {0,0,0,1}; //order of applying is yaw-pitch-roll //yaw is not possible using only accel oquatf_mult_me(&or, &pitch); oquatf_mult_me(&or, &roll); me->orient = or; } //shorter buffers for frame smoothing static void nofusion_init(fusion* me) { memset(me, 0, sizeof(fusion)); me->orient.w = 1.0f; ofq_init(&me->mag_fq, 10); ofq_init(&me->accel_fq, 10); ofq_init(&me->ang_vel_fq, 10); me->flags = FF_USE_GRAVITY; me->grav_gain = 0.05f; } static void set_android_properties(ohmd_device* device, ohmd_device_properties* props) { android_priv* priv = (android_priv*)device; priv->base.properties.hsize = props->hsize; priv->base.properties.vsize = props->vsize; priv->base.properties.hres = props->hres; priv->base.properties.vres = props->vres; priv->base.properties.lens_sep = props->lens_sep; priv->base.properties.lens_vpos = props->lens_vpos; priv->base.properties.fov = DEG_TO_RAD(props->fov); priv->base.properties.ratio = props->ratio; } static void set_android_profile(ohmd_driver* driver, android_hmd_profile profile) { switch(profile){ case DROID_DUROVIS_OPEN_DIVE: break; case DROID_DUROVIS_DIVE_5: break; case DROID_DUROVIS_DIVE_7: break; case DROID_CARL_ZEISS_VRONE: break; case DROID_GOOGLE_CARDBOARD: break; case DROID_NONE: default: break; } } OpenHMD-0.2.0/src/drv_android/android.h000066400000000000000000000037101277204664200176310ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Copyright (C) 2015 Joey Ferwerda * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Android Driver */ #ifndef ANDROID_H #define ANDROID_H #include "../openhmdi.h" typedef enum { DROID_DUROVIS_OPEN_DIVE = 1, DROID_DUROVIS_DIVE_5 = 2, DROID_DUROVIS_DIVE_7 = 3, DROID_CARL_ZEISS_VRONE = 4, DROID_GOOGLE_CARDBOARD = 5, DROID_NONE = 0, } android_hmd_profile; //Android copy-paste from android_native_app_glue to be able to cast data to something useful #include #include #include #include #include #include struct android_app; struct android_poll_source { int32_t id; struct android_app* app; void (*process)(struct android_app* app, struct android_poll_source* source); }; typedef struct android_app { void* userData; void (*onAppCmd)(struct android_app* app, int32_t cmd); int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); ANativeActivity* activity; AConfiguration* config; void* savedState; size_t savedStateSize; ALooper* looper; AInputQueue* inputQueue; ANativeWindow* window; ARect contentRect; int activityState; int destroyRequested; pthread_mutex_t mutex; pthread_cond_t cond; int msgread; int msgwrite; pthread_t thread; struct android_poll_source cmdPollSource; struct android_poll_source inputPollSource; int running; int stateSaved; int destroyed; int redrawNeeded; AInputQueue* pendingInputQueue; ANativeWindow* pendingWindow; ARect pendingContentRect; } android_app; enum { LOOPER_ID_MAIN = 1, LOOPER_ID_INPUT = 2, LOOPER_ID_USER = 3 }; #endif // ANDROID_H OpenHMD-0.2.0/src/drv_dummy/000077500000000000000000000000001277204664200155525ustar00rootroot00000000000000OpenHMD-0.2.0/src/drv_dummy/dummy.c000066400000000000000000000053271277204664200170600ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Dummy Driver */ #include #include "../openhmdi.h" typedef struct { ohmd_device base; } dummy_priv; static void update_device(ohmd_device* device) { } static int getf(ohmd_device* device, ohmd_float_value type, float* out) { dummy_priv* priv = (dummy_priv*)device; switch(type){ case OHMD_ROTATION_QUAT: out[0] = out[1] = out[2] = 0; out[3] = 1.0f; break; case OHMD_POSITION_VECTOR: out[0] = out[1] = out[2] = 0; break; case OHMD_DISTORTION_K: // TODO this should be set to the equivalent of no distortion memset(out, 0, sizeof(float) * 6); break; default: ohmd_set_error(priv->base.ctx, "invalid type given to getf (%ud)", type); return -1; break; } return 0; } static void close_device(ohmd_device* device) { LOGD("closing dummy device"); free(device); } static ohmd_device* open_device(ohmd_driver* driver, ohmd_device_desc* desc) { dummy_priv* priv = ohmd_alloc(driver->ctx, sizeof(dummy_priv)); if(!priv) return NULL; // Set default device properties ohmd_set_default_device_properties(&priv->base.properties); // Set device properties (imitates the rift values) priv->base.properties.hsize = 0.149760f; priv->base.properties.vsize = 0.093600f; priv->base.properties.hres = 1280; priv->base.properties.vres = 800; priv->base.properties.lens_sep = 0.063500; priv->base.properties.lens_vpos = 0.046800; priv->base.properties.fov = DEG_TO_RAD(125.5144f); priv->base.properties.ratio = (1280.0f / 800.0f) / 2.0f; // calculate projection eye projection matrices from the device properties ohmd_calc_default_proj_matrices(&priv->base.properties); // set up device callbacks priv->base.update = update_device; priv->base.close = close_device; priv->base.getf = getf; return (ohmd_device*)priv; } static void get_device_list(ohmd_driver* driver, ohmd_device_list* list) { ohmd_device_desc* desc = &list->devices[list->num_devices++]; strcpy(desc->driver, "OpenHMD Dummy Driver"); strcpy(desc->vendor, "OpenHMD"); strcpy(desc->product, "Dummy Device"); strcpy(desc->path, "(none)"); desc->driver_ptr = driver; } static void destroy_driver(ohmd_driver* drv) { LOGD("shutting down dummy driver"); free(drv); } ohmd_driver* ohmd_create_dummy_drv(ohmd_context* ctx) { ohmd_driver* drv = ohmd_alloc(ctx, sizeof(ohmd_driver)); if(!drv) return NULL; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->destroy = destroy_driver; return drv; } OpenHMD-0.2.0/src/drv_external/000077500000000000000000000000001277204664200162415ustar00rootroot00000000000000OpenHMD-0.2.0/src/drv_external/external.c000066400000000000000000000074511277204664200202360ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Copyright (C) 2015 Joey Ferwerda * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* External Driver */ #include "../openhmdi.h" #include "string.h" typedef struct { ohmd_device base; fusion sensor_fusion; } external_priv; static void update_device(ohmd_device* device) { } static int getf(ohmd_device* device, ohmd_float_value type, float* out) { external_priv* priv = (external_priv*)device; switch(type){ case OHMD_ROTATION_QUAT: { *(quatf*)out = priv->sensor_fusion.orient; break; } case OHMD_POSITION_VECTOR: out[0] = out[1] = out[2] = 0; break; default: ohmd_set_error(priv->base.ctx, "invalid type given to getf (%d)", type); return -1; break; } return 0; } static int setf(ohmd_device* device, ohmd_float_value type, const float* in) { external_priv* priv = (external_priv*)device; switch(type){ case OHMD_EXTERNAL_SENSOR_FUSION: { ofusion_update(&priv->sensor_fusion, *in, (vec3f*)(in + 1), (vec3f*)(in + 4), (vec3f*)(in + 7)); } break; default: ohmd_set_error(priv->base.ctx, "invalid type given to setf (%d)", type); return -1; break; } return 0; } static void close_device(ohmd_device* device) { LOGD("closing external device"); free(device); } static ohmd_device* open_device(ohmd_driver* driver, ohmd_device_desc* desc) { external_priv* priv = ohmd_alloc(driver->ctx, sizeof(external_priv)); if(!priv) return NULL; // Set default device properties ohmd_set_default_device_properties(&priv->base.properties); // Set device properties //TODO: Get information from external device using set_external_properties? //Using 'dummy' settings for now priv->base.properties.hsize = 0.149760f; priv->base.properties.vsize = 0.093600f; priv->base.properties.hres = 1280; priv->base.properties.vres = 800; priv->base.properties.lens_sep = 0.063500; priv->base.properties.lens_vpos = 0.046800; priv->base.properties.fov = DEG_TO_RAD(125.5144f); priv->base.properties.ratio = (1280.0f / 800.0f) / 2.0f; // calculate projection eye projection matrices from the device properties ohmd_calc_default_proj_matrices(&priv->base.properties); // set up device callbacks priv->base.update = update_device; priv->base.close = close_device; priv->base.getf = getf; priv->base.setf = setf; ofusion_init(&priv->sensor_fusion); return (ohmd_device*)priv; } static void get_device_list(ohmd_driver* driver, ohmd_device_list* list) { ohmd_device_desc* desc = &list->devices[list->num_devices++]; strcpy(desc->driver, "OpenHMD Generic External Driver"); strcpy(desc->vendor, "OpenHMD"); strcpy(desc->product, "External Device"); strcpy(desc->path, "(none)"); desc->driver_ptr = driver; } static void destroy_driver(ohmd_driver* drv) { LOGD("shutting down external driver"); free(drv); } ohmd_driver* ohmd_create_external_drv(ohmd_context* ctx) { ohmd_driver* drv = ohmd_alloc(ctx, sizeof(ohmd_driver)); if(!drv) return NULL; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->destroy = destroy_driver; return drv; } /* external specific functions */ /*static void set_external_properties(ohmd_device* device, ohmd_device_properties* props) { external_priv* priv = (external_priv*)device; priv->base.properties.hsize = props->hsize; priv->base.properties.vsize = props->vsize; priv->base.properties.hres = props->hres; priv->base.properties.vres = props->vres; priv->base.properties.lens_sep = props->lens_sep; priv->base.properties.lens_vpos = props->lens_vpos; priv->base.properties.fov = DEG_TO_RAD(props->fov); priv->base.properties.ratio = props->ratio; }*/ OpenHMD-0.2.0/src/drv_oculus_rift/000077500000000000000000000000001277204664200167555ustar00rootroot00000000000000OpenHMD-0.2.0/src/drv_oculus_rift/packet.c000066400000000000000000000160641277204664200203770ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Oculus Rift Driver - Packet Decoding and Utilities */ #include #include "rift.h" #define SKIP_CMD (buffer++) #define READ8 *(buffer++); #define READ16 *buffer | (*(buffer + 1) << 8); buffer += 2; #define READ32 *buffer | (*(buffer + 1) << 8) | (*(buffer + 2) << 16) | (*(buffer + 3) << 24); buffer += 4; #define READFLOAT ((float)(*buffer)); buffer += 4; #define READFIXED (float)(*buffer | (*(buffer + 1) << 8) | (*(buffer + 2) << 16) | (*(buffer + 3) << 24)) / 1000000.0f; buffer += 4; #define WRITE8(_val) *(buffer++) = (_val); #define WRITE16(_val) WRITE8((_val) & 0xff); WRITE8(((_val) >> 8) & 0xff); #define WRITE32(_val) WRITE16((_val) & 0xffff) *buffer; WRITE16(((_val) >> 16) & 0xffff); bool decode_sensor_range(pkt_sensor_range* range, const unsigned char* buffer, int size) { if(!(size == 8 || size == 9)){ LOGE("invalid packet size (expected 8 or 9 but got %d)", size); return false; } SKIP_CMD; range->command_id = READ16; range->accel_scale = READ8; range->gyro_scale = READ16; range->mag_scale = READ16; return true; } bool decode_sensor_display_info(pkt_sensor_display_info* info, const unsigned char* buffer, int size) { if(!(size == 56 || size == 57)){ LOGE("invalid packet size (expected 56 or 57 but got %d)", size); return false; } SKIP_CMD; info->command_id = READ16; info->distortion_type = READ8; info->h_resolution = READ16; info->v_resolution = READ16; info->h_screen_size = READFIXED; info->v_screen_size = READFIXED; info->v_center = READFIXED; info->lens_separation = READFIXED; info->eye_to_screen_distance[0] = READFIXED; info->eye_to_screen_distance[1] = READFIXED; info->distortion_type_opts = 0; for(int i = 0; i < 6; i++) info->distortion_k[i] = READFLOAT; return true; } bool decode_sensor_config(pkt_sensor_config* config, const unsigned char* buffer, int size) { if(!(size == 7 || size == 8)){ LOGE("invalid packet size (expected 7 or 8 but got %d)", size); return false; } SKIP_CMD; config->command_id = READ16; config->flags = READ8; config->packet_interval = READ8; config->keep_alive_interval = READ16; return true; } static void decode_sample(const unsigned char* buffer, int32_t* smp) { /* * Decode 3 tightly packed 21 bit values from 4 bytes. * We unpack them in the higher 21 bit values first and then shift * them down to the lower in order to get the sign bits correct. */ int x = (buffer[0] << 24) | (buffer[1] << 16) | ((buffer[2] & 0xF8) << 8); int y = ((buffer[2] & 0x07) << 29) | (buffer[3] << 21) | (buffer[4] << 13) | ((buffer[5] & 0xC0) << 5); int z = ((buffer[5] & 0x3F) << 26) | (buffer[6] << 18) | (buffer[7] << 10); smp[0] = x >> 11; smp[1] = y >> 11; smp[2] = z >> 11; } bool decode_tracker_sensor_msg(pkt_tracker_sensor* msg, const unsigned char* buffer, int size) { if(!(size == 62 || size == 64)){ LOGE("invalid packet size (expected 62 or 64 but got %d)", size); return false; } SKIP_CMD; msg->num_samples = READ8; msg->timestamp = READ16; msg->last_command_id = READ16; msg->temperature = READ16; int actual = OHMD_MIN(msg->num_samples, 3); for(int i = 0; i < actual; i++){ decode_sample(buffer, msg->samples[i].accel); buffer += 8; decode_sample(buffer, msg->samples[i].gyro); buffer += 8; } // Skip empty samples buffer += (3 - actual) * 16; for(int i = 0; i < 3; i++){ msg->mag[i] = READ16; } return true; } // TODO do we need to consider HMD vs sensor "centric" values void vec3f_from_rift_vec(const int32_t* smp, vec3f* out_vec) { out_vec->x = (float)smp[0] * 0.0001f; out_vec->y = (float)smp[1] * 0.0001f; out_vec->z = (float)smp[2] * 0.0001f; } int encode_sensor_config(unsigned char* buffer, const pkt_sensor_config* config) { WRITE8(RIFT_CMD_SENSOR_CONFIG); WRITE16(config->command_id); WRITE8(config->flags); WRITE8(config->packet_interval); WRITE16(config->keep_alive_interval); return 7; // sensor config packet size } int encode_keep_alive(unsigned char* buffer, const pkt_keep_alive* keep_alive) { WRITE8(RIFT_CMD_KEEP_ALIVE); WRITE16(keep_alive->command_id); WRITE16(keep_alive->keep_alive_interval); return 5; // keep alive packet size } void dump_packet_sensor_range(const pkt_sensor_range* range) { (void)range; LOGD("sensor range\n"); LOGD(" command id: %d", range->command_id); LOGD(" accel scale: %d", range->accel_scale); LOGD(" gyro scale: %d", range->gyro_scale); LOGD(" mag scale: %d", range->mag_scale); } void dump_packet_sensor_display_info(const pkt_sensor_display_info* info) { (void)info; LOGD("display info"); LOGD(" command id: %d", info->command_id); LOGD(" distortion_type: %d", info->distortion_type); LOGD(" resolution: %d x %d", info->h_resolution, info->v_resolution); LOGD(" screen size: %f x %f", info->h_screen_size, info->v_screen_size); LOGD(" vertical center: %f", info->v_center); LOGD(" lens_separation: %f", info->lens_separation); LOGD(" eye_to_screen_distance: %f, %f", info->eye_to_screen_distance[0], info->eye_to_screen_distance[1]); LOGD(" distortion_k: %f, %f, %f, %f, %f, %f", info->distortion_k[0], info->distortion_k[1], info->distortion_k[2], info->distortion_k[3], info->distortion_k[4], info->distortion_k[5]); } void dump_packet_sensor_config(const pkt_sensor_config* config) { (void)config; LOGD("sensor config"); LOGD(" command id: %u", config->command_id); LOGD(" flags: %02x", config->flags); LOGD(" raw mode: %d", !!(config->flags & RIFT_SCF_RAW_MODE)); LOGD(" calibration test: %d", !!(config->flags & RIFT_SCF_CALIBRATION_TEST)); LOGD(" use calibration: %d", !!(config->flags & RIFT_SCF_USE_CALIBRATION)); LOGD(" auto calibration: %d", !!(config->flags & RIFT_SCF_AUTO_CALIBRATION)); LOGD(" motion keep alive: %d", !!(config->flags & RIFT_SCF_MOTION_KEEP_ALIVE)); LOGD(" motion command keep alive: %d", !!(config->flags & RIFT_SCF_COMMAND_KEEP_ALIVE)); LOGD(" sensor coordinates: %d", !!(config->flags & RIFT_SCF_SENSOR_COORDINATES)); LOGD(" packet interval: %u", config->packet_interval); LOGD(" keep alive interval: %u", config->keep_alive_interval); } void dump_packet_tracker_sensor(const pkt_tracker_sensor* sensor) { (void)sensor; LOGD("tracker sensor:"); LOGD(" last command id: %u", sensor->last_command_id); LOGD(" timestamp: %u", sensor->timestamp); LOGD(" temperature: %d", sensor->temperature); LOGD(" num samples: %u", sensor->num_samples); LOGD(" magnetic field: %i %i %i", sensor->mag[0], sensor->mag[1], sensor->mag[2]); for(int i = 0; i < OHMD_MIN(sensor->num_samples, 3); i++){ LOGD(" accel: %d %d %d", sensor->samples[i].accel[0], sensor->samples[i].accel[1], sensor->samples[i].accel[2]); LOGD(" gyro: %d %d %d", sensor->samples[i].gyro[0], sensor->samples[i].gyro[1], sensor->samples[i].gyro[2]); } } OpenHMD-0.2.0/src/drv_oculus_rift/rift.c000066400000000000000000000242011277204664200200640ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Oculus Rift Driver - HID/USB Driver Implementation */ #include #include #include #include #include #include #include "rift.h" #define TICK_LEN (1.0f / 1000.0f) // 1000 Hz ticks #define KEEP_ALIVE_VALUE (10 * 1000) #define SETFLAG(_s, _flag, _val) (_s) = ((_s) & ~(_flag)) | ((_val) ? (_flag) : 0) typedef struct { ohmd_device base; hid_device* handle; pkt_sensor_range sensor_range; pkt_sensor_display_info display_info; rift_coordinate_frame coordinate_frame, hw_coordinate_frame; pkt_sensor_config sensor_config; pkt_tracker_sensor sensor; double last_keep_alive; fusion sensor_fusion; vec3f raw_mag, raw_accel, raw_gyro; } rift_priv; static rift_priv* rift_priv_get(ohmd_device* device) { return (rift_priv*)device; } static int get_feature_report(rift_priv* priv, rift_sensor_feature_cmd cmd, unsigned char* buf) { memset(buf, 0, FEATURE_BUFFER_SIZE); buf[0] = (unsigned char)cmd; return hid_get_feature_report(priv->handle, buf, FEATURE_BUFFER_SIZE); } static int send_feature_report(rift_priv* priv, const unsigned char *data, size_t length) { return hid_send_feature_report(priv->handle, data, length); } static void set_coordinate_frame(rift_priv* priv, rift_coordinate_frame coordframe) { priv->coordinate_frame = coordframe; // set the RIFT_SCF_SENSOR_COORDINATES in the sensor config to match whether coordframe is hmd or sensor SETFLAG(priv->sensor_config.flags, RIFT_SCF_SENSOR_COORDINATES, coordframe == RIFT_CF_SENSOR); // encode send the new config to the Rift unsigned char buf[FEATURE_BUFFER_SIZE]; int size = encode_sensor_config(buf, &priv->sensor_config); if(send_feature_report(priv, buf, size) == -1){ ohmd_set_error(priv->base.ctx, "send_feature_report failed in set_coordinate frame"); return; } // read the state again, set the hw_coordinate_frame to match what // the hardware actually is set to just incase it doesn't stick. size = get_feature_report(priv, RIFT_CMD_SENSOR_CONFIG, buf); if(size <= 0){ LOGW("could not set coordinate frame"); priv->hw_coordinate_frame = RIFT_CF_HMD; return; } decode_sensor_config(&priv->sensor_config, buf, size); priv->hw_coordinate_frame = (priv->sensor_config.flags & RIFT_SCF_SENSOR_COORDINATES) ? RIFT_CF_SENSOR : RIFT_CF_HMD; if(priv->hw_coordinate_frame != coordframe) { LOGW("coordinate frame didn't stick"); } } static void handle_tracker_sensor_msg(rift_priv* priv, unsigned char* buffer, int size) { if(!decode_tracker_sensor_msg(&priv->sensor, buffer, size)){ LOGE("couldn't decode tracker sensor message"); } pkt_tracker_sensor* s = &priv->sensor; dump_packet_tracker_sensor(s); // TODO handle missed samples etc. float dt = s->num_samples > 3 ? (s->num_samples - 2) * TICK_LEN : TICK_LEN; int32_t mag32[] = { s->mag[0], s->mag[1], s->mag[2] }; vec3f_from_rift_vec(mag32, &priv->raw_mag); for(int i = 0; i < OHMD_MIN(s->num_samples, 3); i++){ vec3f_from_rift_vec(s->samples[i].accel, &priv->raw_accel); vec3f_from_rift_vec(s->samples[i].gyro, &priv->raw_gyro); ofusion_update(&priv->sensor_fusion, dt, &priv->raw_gyro, &priv->raw_accel, &priv->raw_mag); // reset dt to tick_len for the last samples if there were more than one sample dt = TICK_LEN; } } static void update_device(ohmd_device* device) { rift_priv* priv = rift_priv_get(device); unsigned char buffer[FEATURE_BUFFER_SIZE]; // Handle keep alive messages double t = ohmd_get_tick(); if(t - priv->last_keep_alive >= (double)priv->sensor_config.keep_alive_interval / 1000.0 - .2){ // send keep alive message pkt_keep_alive keep_alive = { 0, priv->sensor_config.keep_alive_interval }; int ka_size = encode_keep_alive(buffer, &keep_alive); send_feature_report(priv, buffer, ka_size); // Update the time of the last keep alive we have sent. priv->last_keep_alive = t; } // Read all the messages from the device. while(true){ int size = hid_read(priv->handle, buffer, FEATURE_BUFFER_SIZE); if(size < 0){ LOGE("error reading from device"); return; } else if(size == 0) { return; // No more messages, return. } // currently the only message type the hardware supports (I think) if(buffer[0] == RIFT_IRQ_SENSORS){ handle_tracker_sensor_msg(priv, buffer, size); }else{ LOGE("unknown message type: %u", buffer[0]); } } } static int getf(ohmd_device* device, ohmd_float_value type, float* out) { rift_priv* priv = rift_priv_get(device); switch(type){ case OHMD_DISTORTION_K: { for (int i = 0; i < 6; i++) { out[i] = priv->display_info.distortion_k[i]; } break; } case OHMD_ROTATION_QUAT: { *(quatf*)out = priv->sensor_fusion.orient; break; } case OHMD_POSITION_VECTOR: out[0] = out[1] = out[2] = 0; break; default: ohmd_set_error(priv->base.ctx, "invalid type given to getf (%ud)", type); return -1; break; } return 0; } static void close_device(ohmd_device* device) { LOGD("closing device"); rift_priv* priv = rift_priv_get(device); hid_close(priv->handle); free(priv); } static char* _hid_to_unix_path(char* path) { char bus [4]; char dev [4]; char *result = malloc( sizeof(char) * ( 20 + 1 ) ); sprintf (bus, "%.*s\n", 4, path); sprintf (dev, "%.*s\n", 4, path + 5); sprintf (result, "/dev/bus/usb/%03d/%03d", (int)strtol(bus, NULL, 16), (int)strtol(dev, NULL, 16)); return result; } static ohmd_device* open_device(ohmd_driver* driver, ohmd_device_desc* desc) { rift_priv* priv = ohmd_alloc(driver->ctx, sizeof(rift_priv)); if(!priv) goto cleanup; priv->base.ctx = driver->ctx; // Open the HID device priv->handle = hid_open_path(desc->path); if(!priv->handle) { char* path = _hid_to_unix_path(desc->path); ohmd_set_error(driver->ctx, "Could not open %s. " "Check your rights.", path); free(path); goto cleanup; } if(hid_set_nonblocking(priv->handle, 1) == -1){ ohmd_set_error(driver->ctx, "failed to set non-blocking on device"); goto cleanup; } unsigned char buf[FEATURE_BUFFER_SIZE]; int size; // Read and decode the sensor range size = get_feature_report(priv, RIFT_CMD_RANGE, buf); decode_sensor_range(&priv->sensor_range, buf, size); dump_packet_sensor_range(&priv->sensor_range); // Read and decode display information size = get_feature_report(priv, RIFT_CMD_DISPLAY_INFO, buf); decode_sensor_display_info(&priv->display_info, buf, size); dump_packet_sensor_display_info(&priv->display_info); // Read and decode the sensor config size = get_feature_report(priv, RIFT_CMD_SENSOR_CONFIG, buf); decode_sensor_config(&priv->sensor_config, buf, size); dump_packet_sensor_config(&priv->sensor_config); // if the sensor has display info data, use HMD coordinate frame priv->coordinate_frame = priv->display_info.distortion_type != RIFT_DT_NONE ? RIFT_CF_HMD : RIFT_CF_SENSOR; // enable calibration SETFLAG(priv->sensor_config.flags, RIFT_SCF_USE_CALIBRATION, 1); SETFLAG(priv->sensor_config.flags, RIFT_SCF_AUTO_CALIBRATION, 1); // apply sensor config set_coordinate_frame(priv, priv->coordinate_frame); // set keep alive interval to n seconds pkt_keep_alive keep_alive = { 0, KEEP_ALIVE_VALUE }; size = encode_keep_alive(buf, &keep_alive); send_feature_report(priv, buf, size); // Update the time of the last keep alive we have sent. priv->last_keep_alive = ohmd_get_tick(); // update sensor settings with new keep alive value // (which will have been ignored in favor of the default 1000 ms one) size = get_feature_report(priv, RIFT_CMD_SENSOR_CONFIG, buf); decode_sensor_config(&priv->sensor_config, buf, size); dump_packet_sensor_config(&priv->sensor_config); // Set default device properties ohmd_set_default_device_properties(&priv->base.properties); // Set device properties priv->base.properties.hsize = priv->display_info.h_screen_size; priv->base.properties.vsize = priv->display_info.v_screen_size; priv->base.properties.hres = priv->display_info.h_resolution; priv->base.properties.vres = priv->display_info.v_resolution; priv->base.properties.lens_sep = priv->display_info.lens_separation; priv->base.properties.lens_vpos = priv->display_info.v_center; priv->base.properties.fov = DEG_TO_RAD(125.5144f); // TODO calculate. priv->base.properties.ratio = ((float)priv->display_info.h_resolution / (float)priv->display_info.v_resolution) / 2.0f; // calculate projection eye projection matrices from the device properties ohmd_calc_default_proj_matrices(&priv->base.properties); // set up device callbacks priv->base.update = update_device; priv->base.close = close_device; priv->base.getf = getf; // initialize sensor fusion ofusion_init(&priv->sensor_fusion); return &priv->base; cleanup: if(priv) free(priv); return NULL; } #define OCULUS_VR_INC_ID 0x2833 #define RIFT_ID_COUNT 3 static void get_device_list(ohmd_driver* driver, ohmd_device_list* list) { // enumerate HID devices and add any Rifts found to the device list int ids[RIFT_ID_COUNT] = { 0x0001 /* DK1 */, 0x0021 /* DK2 */, 0x2021 /* DK2 alternative id */, }; for(int i = 0; i < RIFT_ID_COUNT; i++){ struct hid_device_info* devs = hid_enumerate(OCULUS_VR_INC_ID, ids[i]); struct hid_device_info* cur_dev = devs; if(devs == NULL) continue; while (cur_dev) { ohmd_device_desc* desc = &list->devices[list->num_devices++]; strcpy(desc->driver, "OpenHMD Rift Driver"); strcpy(desc->vendor, "Oculus VR, Inc."); strcpy(desc->product, "Rift (Devkit)"); desc->revision = i; strcpy(desc->path, cur_dev->path); desc->driver_ptr = driver; cur_dev = cur_dev->next; } hid_free_enumeration(devs); } } static void destroy_driver(ohmd_driver* drv) { LOGD("shutting down driver"); hid_exit(); free(drv); } ohmd_driver* ohmd_create_oculus_rift_drv(ohmd_context* ctx) { ohmd_driver* drv = ohmd_alloc(ctx, sizeof(ohmd_driver)); if(drv == NULL) return NULL; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->ctx = ctx; drv->get_device_list = get_device_list; drv->open_device = open_device; drv->destroy = destroy_driver; return drv; } OpenHMD-0.2.0/src/drv_oculus_rift/rift.h000066400000000000000000000054751277204664200201050ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Oculus Rift Driver Internal Interface */ #ifndef RIFT_H #define RIFT_H #include "../openhmdi.h" #define FEATURE_BUFFER_SIZE 256 typedef enum { RIFT_CMD_SENSOR_CONFIG = 2, RIFT_CMD_RANGE = 4, RIFT_CMD_KEEP_ALIVE = 8, RIFT_CMD_DISPLAY_INFO = 9 } rift_sensor_feature_cmd; typedef enum { RIFT_CF_SENSOR, RIFT_CF_HMD } rift_coordinate_frame; typedef enum { RIFT_IRQ_SENSORS = 1 } rift_irq_cmd; typedef enum { RIFT_DT_NONE, RIFT_DT_SCREEN_ONLY, RIFT_DT_DISTORTION } rift_distortion_type; // Sensor config flags #define RIFT_SCF_RAW_MODE 0x01 #define RIFT_SCF_CALIBRATION_TEST 0x02 #define RIFT_SCF_USE_CALIBRATION 0x04 #define RIFT_SCF_AUTO_CALIBRATION 0x08 #define RIFT_SCF_MOTION_KEEP_ALIVE 0x10 #define RIFT_SCF_COMMAND_KEEP_ALIVE 0x20 #define RIFT_SCF_SENSOR_COORDINATES 0x40 typedef struct { uint16_t command_id; uint16_t accel_scale; uint16_t gyro_scale; uint16_t mag_scale; } pkt_sensor_range; typedef struct { int32_t accel[3]; int32_t gyro[3]; } pkt_tracker_sample; typedef struct { uint8_t num_samples; uint16_t timestamp; uint16_t last_command_id; int16_t temperature; pkt_tracker_sample samples[3]; int16_t mag[3]; } pkt_tracker_sensor; typedef struct { uint16_t command_id; uint8_t flags; uint16_t packet_interval; uint16_t keep_alive_interval; // in ms } pkt_sensor_config; typedef struct { uint16_t command_id; rift_distortion_type distortion_type; uint8_t distortion_type_opts; uint16_t h_resolution, v_resolution; float h_screen_size, v_screen_size; float v_center; float lens_separation; float eye_to_screen_distance[2]; float distortion_k[6]; } pkt_sensor_display_info; typedef struct { uint16_t command_id; uint16_t keep_alive_interval; } pkt_keep_alive; bool decode_sensor_range(pkt_sensor_range* range, const unsigned char* buffer, int size); bool decode_sensor_display_info(pkt_sensor_display_info* info, const unsigned char* buffer, int size); bool decode_sensor_config(pkt_sensor_config* config, const unsigned char* buffer, int size); bool decode_tracker_sensor_msg(pkt_tracker_sensor* msg, const unsigned char* buffer, int size); void vec3f_from_rift_vec(const int32_t* smp, vec3f* out_vec); int encode_sensor_config(unsigned char* buffer, const pkt_sensor_config* config); int encode_keep_alive(unsigned char* buffer, const pkt_keep_alive* keep_alive); void dump_packet_sensor_range(const pkt_sensor_range* range); void dump_packet_sensor_config(const pkt_sensor_config* config); void dump_packet_sensor_display_info(const pkt_sensor_display_info* info); void dump_packet_tracker_sensor(const pkt_tracker_sensor* sensor); #endif OpenHMD-0.2.0/src/fusion.c000066400000000000000000000066451277204664200152260ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Sensor Fusion Implementation */ #include #include "openhmdi.h" void ofusion_init(fusion* me) { memset(me, 0, sizeof(fusion)); me->orient.w = 1.0f; ofq_init(&me->mag_fq, 20); ofq_init(&me->accel_fq, 20); ofq_init(&me->ang_vel_fq, 20); me->flags = FF_USE_GRAVITY; me->grav_gain = 0.05f; } void ofusion_update(fusion* me, float dt, const vec3f* ang_vel, const vec3f* accel, const vec3f* mag) { me->ang_vel = *ang_vel; me->accel = *accel; me->raw_mag = *mag; me->mag = *mag; vec3f world_accel; oquatf_get_rotated(&me->orient, accel, &world_accel); me->iterations += 1; me->time += dt; ofq_add(&me->mag_fq, mag); ofq_add(&me->accel_fq, &world_accel); ofq_add(&me->ang_vel_fq, ang_vel); float ang_vel_length = ovec3f_get_length(ang_vel); if(ang_vel_length > 0.0001f){ vec3f rot_axis = {{ ang_vel->x / ang_vel_length, ang_vel->y / ang_vel_length, ang_vel->z / ang_vel_length }}; float rot_angle = ang_vel_length * dt; quatf delta_orient; oquatf_init_axis(&delta_orient, &rot_axis, rot_angle); oquatf_mult_me(&me->orient, &delta_orient); } // gravity correction if(me->flags & FF_USE_GRAVITY){ const float gravity_tolerance = .4f, ang_vel_tolerance = .1f; const float min_tilt_error = 0.05f, max_tilt_error = 0.01f; // if the device is within tolerance levels, count this as the device is level and add to the counter // otherwise reset the counter and start over me->device_level_count = fabsf(ovec3f_get_length(accel) - 9.82f) < gravity_tolerance && ang_vel_length < ang_vel_tolerance ? me->device_level_count + 1 : 0; // device has been level for long enough, grab mean from the accelerometer filter queue (last n values) // and use for correction if(me->device_level_count > 50){ me->device_level_count = 0; vec3f accel_mean; ofq_get_mean(&me->accel_fq, &accel_mean); // Calculate a cross product between what the device // thinks is up and what gravity indicates is down. // The values are optimized of what we would get out // from the cross product. vec3f tilt = {{accel_mean.z, 0, -accel_mean.x}}; ovec3f_normalize_me(&tilt); ovec3f_normalize_me(&accel_mean); vec3f up = {{0, 1.0f, 0}}; float tilt_angle = ovec3f_get_angle(&up, &accel_mean); if(tilt_angle > max_tilt_error){ me->grav_error_angle = tilt_angle; me->grav_error_axis = tilt; } } // preform gravity tilt correction if(me->grav_error_angle > min_tilt_error){ float use_angle; // if less than 2000 iterations have passed, set the up axis to the correction value outright if(me->grav_error_angle > gravity_tolerance && me->iterations < 2000){ use_angle = -me->grav_error_angle; me->grav_error_angle = 0; } // otherwise try to correct else { use_angle = -me->grav_gain * me->grav_error_angle * 0.005f * (5.0f * ang_vel_length + 1.0f); me->grav_error_angle += use_angle; } // perform the correction quatf corr_quat, old_orient; oquatf_init_axis(&corr_quat, &me->grav_error_axis, use_angle); old_orient = me->orient; oquatf_mult(&corr_quat, &old_orient, &me->orient); } } // mitigate drift due to floating point // inprecision with quat multiplication. oquatf_normalize_me(&me->orient); }OpenHMD-0.2.0/src/fusion.h000066400000000000000000000017551277204664200152300ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Sensor Fusion */ #ifndef FUSION_H #define FUSION_H #include "omath.h" #define FF_USE_GRAVITY 1 typedef struct { int state; quatf orient; // orientation vec3f accel; // acceleration vec3f ang_vel; // angular velocity vec3f mag; // magnetometer vec3f raw_mag; // raw magnetometer values int iterations; float time; int flags; // filter queues for magnetometer, accelerometers and angular velocity filter_queue mag_fq, accel_fq, ang_vel_fq; // gravity correction int device_level_count; float grav_error_angle; vec3f grav_error_axis; float grav_gain; // amount of correction } fusion; void ofusion_init(fusion* me); void ofusion_update(fusion* me, float dt, const vec3f* ang_vel, const vec3f* accel, const vec3f* mag_field); #endif OpenHMD-0.2.0/src/log.h000066400000000000000000000021651277204664200145020ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Logging and Error Handling */ #ifndef LOG_H #define LOG_H void* ohmd_allocfn(ohmd_context* ctx, const char* e_msg, size_t size); #define ohmd_alloc(_ctx, _size) ohmd_allocfn(_ctx, "could not allocate " #_size " bytes of RAM @ " __FILE__ ":" OHMD_STRINGIFY(__LINE__), _size) #ifndef LOGLEVEL #define LOGLEVEL 2 #endif #define LOG(_level, _levelstr, ...) do{ if(_level >= LOGLEVEL){ printf("[%s] ", (_levelstr)); printf(__VA_ARGS__); puts(""); } } while(0) #if LOGLEVEL == 0 #define LOGD(...) LOG(0, "DD", __VA_ARGS__) #else #define LOGD(...) #endif #define LOGV(...) LOG(1, "VV", __VA_ARGS__) #define LOGI(...) LOG(2, "II", __VA_ARGS__) #define LOGW(...) LOG(3, "WW", __VA_ARGS__) #define LOGE(...) LOG(4, "EE", __VA_ARGS__) #ifdef _MSC_VER #define snprintf _snprintf #endif #define ohmd_set_error(_ctx, ...) { snprintf((_ctx)->error_msg, OHMD_STR_SIZE, __VA_ARGS__); LOGE(__VA_ARGS__); } #endif OpenHMD-0.2.0/src/omath.c000066400000000000000000000211301277204664200150150ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Math Code Implementation */ #include #include "openhmdi.h" // vector float ovec3f_get_length(const vec3f* me) { return sqrtf(POW2(me->x) + POW2(me->y) + POW2(me->z)); } void ovec3f_normalize_me(vec3f* me) { if(me->x == 0 && me->y == 0 && me->z == 0) return; float len = ovec3f_get_length(me); me->x /= len; me->y /= len; me->z /= len; } float ovec3f_get_dot(const vec3f* me, const vec3f* vec) { return me->x * vec->x + me->y * vec->y + me->z * vec->z; } float ovec3f_get_angle(const vec3f* me, const vec3f* vec) { float dot = ovec3f_get_dot(me, vec); float lengths = ovec3f_get_length(me) * ovec3f_get_length(vec); if(lengths == 0) return 0; return acosf(dot / lengths); } // quaternion void oquatf_init_axis(quatf* me, const vec3f* vec, float angle) { vec3f norm = *vec; ovec3f_normalize_me(&norm); me->x = norm.x * sinf(angle / 2.0f); me->y = norm.y * sinf(angle / 2.0f); me->z = norm.z * sinf(angle / 2.0f); me->w = cosf(angle / 2.0f); } void oquatf_get_rotated(const quatf* me, const vec3f* vec, vec3f* out_vec) { quatf q = {{vec->x * me->w + vec->z * me->y - vec->y * me->z, vec->y * me->w + vec->x * me->z - vec->z * me->x, vec->z * me->w + vec->y * me->x - vec->x * me->y, vec->x * me->x + vec->y * me->y + vec->z * me->z}}; out_vec->x = me->w * q.x + me->x * q.w + me->y * q.z - me->z * q.y; out_vec->y = me->w * q.y + me->y * q.w + me->z * q.x - me->x * q.z; out_vec->z = me->w * q.z + me->z * q.w + me->x * q.y - me->y * q.x; } void oquatf_mult(const quatf* me, const quatf* q, quatf* out_q) { out_q->x = me->w * q->x + me->x * q->w + me->y * q->z - me->z * q->y; out_q->y = me->w * q->y - me->x * q->z + me->y * q->w + me->z * q->x; out_q->z = me->w * q->z + me->x * q->y - me->y * q->x + me->z * q->w; out_q->w = me->w * q->w - me->x * q->x - me->y * q->y - me->z * q->z; } void oquatf_mult_me(quatf* me, const quatf* q) { quatf tmp = *me; oquatf_mult(&tmp, q, me); } void oquatf_normalize_me(quatf* me) { float len = oquatf_get_length(me); me->x /= len; me->y /= len; me->z /= len; me->w /= len; } float oquatf_get_length(const quatf* me) { return sqrtf(me->x * me->x + me->y * me->y + me->z * me->z + me->w * me->w); } float oquatf_get_dot(const quatf* me, const quatf* q) { return me->x * q->x + me->y * q->y + me->z * q->z + me->w * q->w; } void oquatf_inverse(quatf* me) { float dot = oquatf_get_dot(me, me); // conjugate for(int i = 0; i < 3; i++) me->arr[i] = -me->arr[i]; for(int i = 0; i < 4; i++) me->arr[i] /= dot; } void oquatf_diff(const quatf* me, const quatf* q, quatf* out_q) { quatf inv = *me; oquatf_inverse(&inv); oquatf_mult(&inv, q, out_q); } void oquatf_slerp (float fT, const quatf* rkP, const quatf* rkQ, bool shortestPath, quatf* out_q) { float fCos = oquatf_get_dot(rkP, rkQ); quatf rkT; // Do we need to invert rotation? if (fCos < 0.0f && shortestPath) { fCos = -fCos; rkT = *rkQ; oquatf_inverse(&rkT); } else { rkT = *rkQ; } if (fabsf(fCos) < 1 - 0.001f) { // Standard case (slerp) float fSin = sqrtf(1 - (fCos*fCos)); float fAngle = atan2f(fSin, fCos); float fInvSin = 1.0f / fSin; float fCoeff0 = sin((1.0f - fT) * fAngle) * fInvSin; float fCoeff1 = sin(fT * fAngle) * fInvSin; out_q->x = fCoeff0 * rkP->x + fCoeff1 * rkT.x; out_q->y = fCoeff0 * rkP->y + fCoeff1 * rkT.y; out_q->z = fCoeff0 * rkP->z + fCoeff1 * rkT.z; out_q->w = fCoeff0 * rkP->w + fCoeff1 * rkT.w; //return fCoeff0 * rkP + fCoeff1 * rkT; } else { // There are two situations: // 1. "rkP" and "rkQ" are very close (fCos ~= +1), so we can do a linear // interpolation safely. // 2. "rkP" and "rkQ" are almost inverse of each other (fCos ~= -1), there // are an infinite number of possibilities interpolation. but we haven't // have method to fix this case, so just use linear interpolation here. //Quaternion t = (1.0f - fT) * rkP + fT * rkT; out_q->x = (1.0f - fT) * rkP->x + fT * rkT.x; out_q->y = (1.0f - fT) * rkP->y + fT * rkT.y; out_q->z = (1.0f - fT) * rkP->z + fT * rkT.z; out_q->w = (1.0f - fT) * rkP->w + fT * rkT.w; oquatf_normalize_me(out_q); // taking the complement requires renormalisation //t.normalise(); //return t; } } void oquatf_get_mat4x4(const quatf* me, const vec3f* point, float mat[4][4]) { mat[0][0] = 1 - 2 * me->y * me->y - 2 * me->z * me->z; mat[0][1] = 2 * me->x * me->y - 2 * me->w * me->z; mat[0][2] = 2 * me->x * me->z + 2 * me->w * me->y; mat[0][3] = point->x; mat[1][0] = 2 * me->x * me->y + 2 * me->w * me->z; mat[1][1] = 1 - 2 * me->x * me->x - 2 * me->z * me->z; mat[1][2] = 2 * me->y * me->z - 2 * me->w * me->x; mat[1][3] = point->y; mat[2][0] = 2 * me->x * me->z - 2 * me->w * me->y; mat[2][1] = 2 * me->y * me->z + 2 * me->w * me->x; mat[2][2] = 1 - 2 * me->x * me->x - 2 * me->y * me->y; mat[2][3] = point->z; mat[3][0] = 0; mat[3][1] = 0; mat[3][2] = 0; mat[3][3] = 1; } // matrix void omat4x4f_init_ident(mat4x4f* me) { memset(me, 0, sizeof(*me)); me->m[0][0] = 1.0f; me->m[1][1] = 1.0f; me->m[2][2] = 1.0f; me->m[3][3] = 1.0f; } void omat4x4f_init_perspective(mat4x4f* me, float fovy_rad, float aspect, float znear, float zfar) { float sine, cotangent, delta, half_fov; half_fov = fovy_rad / 2.0f; delta = zfar - znear; sine = sinf(half_fov); if ((delta == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) { omat4x4f_init_ident(me); return; } cotangent = cosf(half_fov) / sine; me->m[0][0] = cotangent / aspect; me->m[0][1] = 0; me->m[0][2] = 0; me->m[0][3] = 0; me->m[1][0] = 0; me->m[1][1] = cotangent; me->m[1][2] = 0; me->m[1][3] = 0; me->m[2][0] = 0; me->m[2][1] = 0; me->m[2][2] = -(zfar + znear) / delta; me->m[2][3] = -2.0f * znear * zfar / delta; me->m[3][0] = 0; me->m[3][1] = 0; me->m[3][2] = -1.0f; me->m[3][3] = 0; } void omat4x4f_init_look_at(mat4x4f* me, const quatf* rot, const vec3f* eye) { quatf q; vec3f p; q.x = -rot->x; q.y = -rot->y; q.z = -rot->z; q.w = rot->w; p.x = -eye->x; p.y = -eye->y; p.z = -eye->z; me->m[0][0] = 1 - 2 * q.y * q.y - 2 * q.z * q.z; me->m[0][1] = 2 * q.x * q.y - 2 * q.w * q.z; me->m[0][2] = 2 * q.x * q.z + 2 * q.w * q.y; me->m[0][3] = p.x * me->m[0][0] + p.y * me->m[0][1] + p.z * me->m[0][2]; me->m[1][0] = 2 * q.x * q.y + 2 * q.w * q.z; me->m[1][1] = 1 - 2 * q.x * q.x - 2 * q.z * q.z; me->m[1][2] = 2 * q.y * q.z - 2 * q.w * q.x; me->m[1][3] = p.x * me->m[1][0] + p.y * me->m[1][1] + p.z * me->m[1][2]; me->m[2][0] = 2 * q.x * q.z - 2 * q.w * q.y; me->m[2][1] = 2 * q.y * q.z + 2 * q.w * q.x; me->m[2][2] = 1 - 2 * q.x * q.x - 2 * q.y * q.y; me->m[2][3] = p.x * me->m[2][0] + p.y * me->m[2][1] + p.z * me->m[2][2]; me->m[3][0] = 0; me->m[3][1] = 0; me->m[3][2] = 0; me->m[3][3] = 1; } void omat4x4f_init_translate(mat4x4f* me, float x, float y, float z) { omat4x4f_init_ident(me); me->m[0][3] = x; me->m[1][3] = y; me->m[2][3] = z; } void omat4x4f_transpose(const mat4x4f* m, mat4x4f* o) { o->m[0][0] = m->m[0][0]; o->m[1][0] = m->m[0][1]; o->m[2][0] = m->m[0][2]; o->m[3][0] = m->m[0][3]; o->m[0][1] = m->m[1][0]; o->m[1][1] = m->m[1][1]; o->m[2][1] = m->m[1][2]; o->m[3][1] = m->m[1][3]; o->m[0][2] = m->m[2][0]; o->m[1][2] = m->m[2][1]; o->m[2][2] = m->m[2][2]; o->m[3][2] = m->m[2][3]; o->m[0][3] = m->m[3][0]; o->m[1][3] = m->m[3][1]; o->m[2][3] = m->m[3][2]; o->m[3][3] = m->m[3][3]; } void omat4x4f_mult(const mat4x4f* l, const mat4x4f* r, mat4x4f *o) { for(int i = 0; i < 4; i++){ float a0 = l->m[i][0], a1 = l->m[i][1], a2 = l->m[i][2], a3 = l->m[i][3]; o->m[i][0] = a0 * r->m[0][0] + a1 * r->m[1][0] + a2 * r->m[2][0] + a3 * r->m[3][0]; o->m[i][1] = a0 * r->m[0][1] + a1 * r->m[1][1] + a2 * r->m[2][1] + a3 * r->m[3][1]; o->m[i][2] = a0 * r->m[0][2] + a1 * r->m[1][2] + a2 * r->m[2][2] + a3 * r->m[3][2]; o->m[i][3] = a0 * r->m[0][3] + a1 * r->m[1][3] + a2 * r->m[2][3] + a3 * r->m[3][3]; } } // filter queue void ofq_init(filter_queue* me, int size) { memset(me, 0, sizeof(filter_queue)); me->size = size; } void ofq_add(filter_queue* me, const vec3f* vec) { me->elems[me->at] = *vec; me->at = ((me->at + 1) % me->size); } void ofq_get_mean(const filter_queue* me, vec3f* vec) { vec->x = vec->y = vec->z = 0; for(int i = 0; i < me->size; i++){ vec->x += me->elems[i].x; vec->y += me->elems[i].y; vec->z += me->elems[i].z; } vec->x /= (float)me->size; vec->y /= (float)me->size; vec->z /= (float)me->size; } OpenHMD-0.2.0/src/omath.h000066400000000000000000000043151277204664200150300ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Math */ #ifndef OMATH_H #define OMATH_H #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define POW2(_x) ((_x) * (_x)) #define RAD_TO_DEG(_r) ((_r) * 360.0f / (2.0f * (float)M_PI)) #define DEG_TO_RAD(_d) ((_d) * (2.0f * (float)M_PI) / 360.0f) // vector typedef union { struct { float x, y, z; }; float arr[3]; } vec3f; void ovec3f_normalize_me(vec3f* me); float ovec3f_get_length(const vec3f* me); float ovec3f_get_angle(const vec3f* me, const vec3f* vec); float ovec3f_get_dot(const vec3f* me, const vec3f* vec); // quaternion typedef union { struct { float x, y, z, w; }; float arr[4]; } quatf; void oquatf_init_axis(quatf* me, const vec3f* vec, float angle); void oquatf_get_rotated(const quatf* me, const vec3f* vec, vec3f* out_vec); void oquatf_mult_me(quatf* me, const quatf* q); void oquatf_mult(const quatf* me, const quatf* q, quatf* out_q); void oquatf_diff(const quatf* me, const quatf* q, quatf* out_q); void oquatf_normalize_me(quatf* me); float oquatf_get_length(const quatf* me); float oquatf_get_dot(const quatf* me, const quatf* q); void oquatf_inverse(quatf* me); void oquatf_get_mat4x4(const quatf* me, const vec3f* point, float mat[4][4]); // matrix typedef union { float m[4][4]; float arr[16]; } mat4x4f; void omat4x4f_init_ident(mat4x4f* me); void omat4x4f_init_perspective(mat4x4f* me, float fov_rad, float aspect, float znear, float zfar); void omat4x4f_init_look_at(mat4x4f* me, const quatf* ret, const vec3f* eye); void omat4x4f_init_translate(mat4x4f* me, float x, float y, float z); void omat4x4f_mult(const mat4x4f* left, const mat4x4f* right, mat4x4f* out_mat); void omat4x4f_transpose(const mat4x4f* me, mat4x4f* out_mat); // filter queue #define FILTER_QUEUE_MAX_SIZE 256 typedef struct { int at, size; vec3f elems[FILTER_QUEUE_MAX_SIZE]; } filter_queue; void ofq_init(filter_queue* me, int size); void ofq_add(filter_queue* me, const vec3f* vec); void ofq_get_mean(const filter_queue* me, vec3f* vec); #endif OpenHMD-0.2.0/src/openhmd.c000066400000000000000000000277641277204664200153620ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Main Lib Implemenation */ #include "openhmdi.h" #include #include #include // Running automatic updates at 144 Hz #define AUTOMATIC_UPDATE_SLEEP (1.0 / 1000.0) ohmd_context* OHMD_APIENTRY ohmd_ctx_create(void) { ohmd_context* ctx = calloc(1, sizeof(ohmd_context)); if(!ctx){ LOGE("could not allocate RAM for context"); return NULL; } #if DRIVER_OCULUS_RIFT ctx->drivers[ctx->num_drivers++] = ohmd_create_oculus_rift_drv(ctx); #endif #if DRIVER_EXTERNAL ctx->drivers[ctx->num_drivers++] = ohmd_create_external_drv(ctx); #endif #if DRIVER_ANDROID ctx->drivers[ctx->num_drivers++] = ohmd_create_android_drv(ctx); #endif // add dummy driver last to make it the lowest priority ctx->drivers[ctx->num_drivers++] = ohmd_create_dummy_drv(ctx); ctx->update_request_quit = false; return ctx; } void OHMD_APIENTRY ohmd_ctx_destroy(ohmd_context* ctx) { ctx->update_request_quit = true; for(int i = 0; i < ctx->num_active_devices; i++){ ctx->active_devices[i]->close(ctx->active_devices[i]); } for(int i = 0; i < ctx->num_drivers; i++){ ctx->drivers[i]->destroy(ctx->drivers[i]); } if(ctx->update_thread){ ohmd_destroy_thread(ctx->update_thread); ohmd_destroy_mutex(ctx->update_mutex); } free(ctx); } void OHMD_APIENTRY ohmd_ctx_update(ohmd_context* ctx) { for(int i = 0; i < ctx->num_active_devices; i++){ ohmd_device* dev = ctx->active_devices[i]; if(!dev->settings.automatic_update && dev->update) dev->update(dev); ohmd_lock_mutex(ctx->update_mutex); dev->getf(dev, OHMD_POSITION_VECTOR, (float*)&dev->position); dev->getf(dev, OHMD_ROTATION_QUAT, (float*)&dev->rotation); ohmd_unlock_mutex(ctx->update_mutex); } } const char* OHMD_APIENTRY ohmd_ctx_get_error(ohmd_context* ctx) { return ctx->error_msg; } int OHMD_APIENTRY ohmd_ctx_probe(ohmd_context* ctx) { memset(&ctx->list, 0, sizeof(ohmd_device_list)); for(int i = 0; i < ctx->num_drivers; i++){ ctx->drivers[i]->get_device_list(ctx->drivers[i], &ctx->list); } return ctx->list.num_devices; } const char* OHMD_APIENTRY ohmd_list_gets(ohmd_context* ctx, int index, ohmd_string_value type) { if(index >= ctx->list.num_devices) return NULL; switch(type){ case OHMD_VENDOR: return ctx->list.devices[index].vendor; case OHMD_PRODUCT: return ctx->list.devices[index].product; case OHMD_PATH: return ctx->list.devices[index].path; default: return NULL; } } static unsigned int ohmd_update_thread(void* arg) { ohmd_context* ctx = (ohmd_context*)arg; while(!ctx->update_request_quit) { ohmd_lock_mutex(ctx->update_mutex); for(int i = 0; i < ctx->num_active_devices; i++){ if(ctx->active_devices[i]->settings.automatic_update && ctx->active_devices[i]->update) ctx->active_devices[i]->update(ctx->active_devices[i]); } ohmd_unlock_mutex(ctx->update_mutex); ohmd_sleep(AUTOMATIC_UPDATE_SLEEP); } return 0; } static void ohmd_set_up_update_thread(ohmd_context* ctx) { if(!ctx->update_thread){ ctx->update_mutex = ohmd_create_mutex(ctx); ctx->update_thread = ohmd_create_thread(ctx, ohmd_update_thread, ctx); } } ohmd_device* OHMD_APIENTRY ohmd_list_open_device_s(ohmd_context* ctx, int index, ohmd_device_settings* settings) { ohmd_lock_mutex(ctx->update_mutex); if(index >= 0 && index < ctx->list.num_devices){ ohmd_device_desc* desc = &ctx->list.devices[index]; ohmd_driver* driver = (ohmd_driver*)desc->driver_ptr; ohmd_device* device = driver->open_device(driver, desc); if (device == NULL) return NULL; device->rotation_correction.w = 1; device->settings = *settings; device->ctx = ctx; device->active_device_idx = ctx->num_active_devices; ctx->active_devices[ctx->num_active_devices++] = device; ohmd_unlock_mutex(ctx->update_mutex); if(device->settings.automatic_update) ohmd_set_up_update_thread(ctx); return device; } ohmd_unlock_mutex(ctx->update_mutex); ohmd_set_error(ctx, "no device with index: %d", index); return NULL; } ohmd_device* OHMD_APIENTRY ohmd_list_open_device(ohmd_context* ctx, int index) { ohmd_device_settings settings; settings.automatic_update = true; return ohmd_list_open_device_s(ctx, index, &settings); } int OHMD_APIENTRY ohmd_close_device(ohmd_device* device) { ohmd_lock_mutex(device->ctx->update_mutex); ohmd_context* ctx = device->ctx; int idx = device->active_device_idx; memmove(ctx->active_devices + idx, ctx->active_devices + idx + 1, sizeof(ohmd_device*) * (ctx->num_active_devices - idx - 1)); device->close(device); ctx->num_active_devices--; for(int i = idx; i < ctx->num_active_devices; i++) ctx->active_devices[i]->active_device_idx--; ohmd_unlock_mutex(device->ctx->update_mutex); return OHMD_S_OK; } static int ohmd_device_getf_unp(ohmd_device* device, ohmd_float_value type, float* out) { switch(type){ case OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX: { vec3f point = {{0, 0, 0}}; quatf rot = device->rotation; quatf tmp = device->rotation_correction; oquatf_mult_me(&tmp, &rot); rot = tmp; mat4x4f orient, world_shift, result; omat4x4f_init_look_at(&orient, &rot, &point); omat4x4f_init_translate(&world_shift, +(device->properties.ipd / 2.0f), 0, 0); omat4x4f_mult(&world_shift, &orient, &result); omat4x4f_transpose(&result, (mat4x4f*)out); return OHMD_S_OK; } case OHMD_RIGHT_EYE_GL_MODELVIEW_MATRIX: { vec3f point = {{0, 0, 0}}; quatf rot = device->rotation; oquatf_mult_me(&rot, &device->rotation_correction); mat4x4f orient, world_shift, result; omat4x4f_init_look_at(&orient, &rot, &point); omat4x4f_init_translate(&world_shift, -(device->properties.ipd / 2.0f), 0, 0); omat4x4f_mult(&world_shift, &orient, &result); omat4x4f_transpose(&result, (mat4x4f*)out); return OHMD_S_OK; } case OHMD_LEFT_EYE_GL_PROJECTION_MATRIX: omat4x4f_transpose(&device->properties.proj_left, (mat4x4f*)out); return OHMD_S_OK; case OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX: omat4x4f_transpose(&device->properties.proj_right, (mat4x4f*)out); return OHMD_S_OK; case OHMD_SCREEN_HORIZONTAL_SIZE: *out = device->properties.hsize; return OHMD_S_OK; case OHMD_SCREEN_VERTICAL_SIZE: *out = device->properties.vsize; return OHMD_S_OK; case OHMD_LENS_HORIZONTAL_SEPARATION: *out = device->properties.lens_sep; return OHMD_S_OK; case OHMD_LENS_VERTICAL_POSITION: *out = device->properties.lens_vpos; return OHMD_S_OK; case OHMD_RIGHT_EYE_FOV: case OHMD_LEFT_EYE_FOV: *out = device->properties.fov; return OHMD_S_OK; case OHMD_RIGHT_EYE_ASPECT_RATIO: case OHMD_LEFT_EYE_ASPECT_RATIO: *out = device->properties.ratio; return OHMD_S_OK; case OHMD_EYE_IPD: *out = device->properties.ipd; return OHMD_S_OK; case OHMD_PROJECTION_ZFAR: *out = device->properties.zfar; return OHMD_S_OK; case OHMD_PROJECTION_ZNEAR: *out = device->properties.znear; return OHMD_S_OK; case OHMD_ROTATION_QUAT: { *(quatf*)out = device->rotation; oquatf_mult_me((quatf*)out, &device->rotation_correction); quatf tmp = device->rotation_correction; oquatf_mult_me(&tmp, (quatf*)out); *(quatf*)out = tmp; return OHMD_S_OK; } case OHMD_POSITION_VECTOR: { *(vec3f*)out = device->position; for(int i = 0; i < 3; i++) out[i] += device->position_correction.arr[i]; return OHMD_S_OK; } default: return device->getf(device, type, out); } } int OHMD_APIENTRY ohmd_device_getf(ohmd_device* device, ohmd_float_value type, float* out) { ohmd_lock_mutex(device->ctx->update_mutex); int ret = ohmd_device_getf_unp(device, type, out); ohmd_unlock_mutex(device->ctx->update_mutex); return ret; } int ohmd_device_setf_unp(ohmd_device* device, ohmd_float_value type, const float* in) { switch(type){ case OHMD_EYE_IPD: device->properties.ipd = *in; return OHMD_S_OK; case OHMD_PROJECTION_ZFAR: device->properties.zfar = *in; return OHMD_S_OK; case OHMD_PROJECTION_ZNEAR: device->properties.znear = *in; return OHMD_S_OK; case OHMD_ROTATION_QUAT: { // adjust rotation correction quatf q; int ret = device->getf(device, OHMD_ROTATION_QUAT, (float*)&q); if(ret != 0){ return ret; } oquatf_diff(&q, (quatf*)in, &device->rotation_correction); return OHMD_S_OK; } case OHMD_POSITION_VECTOR: { // adjust position correction vec3f v; int ret = device->getf(device, OHMD_POSITION_VECTOR, (float*)&v); if(ret != 0){ return ret; } for(int i = 0; i < 3; i++) device->position_correction.arr[i] = in[i] - v.arr[i]; return OHMD_S_OK; } case OHMD_EXTERNAL_SENSOR_FUSION: { if(device->setf == NULL) return OHMD_S_UNSUPPORTED; return device->setf(device, type, in); } default: return OHMD_S_INVALID_PARAMETER; } } int OHMD_APIENTRY ohmd_device_setf(ohmd_device* device, ohmd_float_value type, const float* in) { ohmd_lock_mutex(device->ctx->update_mutex); int ret = ohmd_device_setf_unp(device, type, in); ohmd_unlock_mutex(device->ctx->update_mutex); return ret; } int OHMD_APIENTRY ohmd_device_geti(ohmd_device* device, ohmd_int_value type, int* out) { switch(type){ case OHMD_SCREEN_HORIZONTAL_RESOLUTION: *out = device->properties.hres; return OHMD_S_OK; case OHMD_SCREEN_VERTICAL_RESOLUTION: *out = device->properties.vres; return OHMD_S_OK; default: return OHMD_S_INVALID_PARAMETER; } } int OHMD_APIENTRY ohmd_device_seti(ohmd_device* device, ohmd_int_value type, const int* in) { switch(type){ default: return OHMD_S_INVALID_PARAMETER; } } int ohmd_device_set_data_unp(ohmd_device* device, ohmd_data_value type, const void* in) { switch(type){ case OHMD_DRIVER_DATA: device->set_data(device, OHMD_DRIVER_DATA, in); return OHMD_S_OK; case OHMD_DRIVER_PROPERTIES: device->set_data(device, OHMD_DRIVER_PROPERTIES, in); return OHMD_S_OK; default: return OHMD_S_INVALID_PARAMETER; } } int OHMD_APIENTRY ohmd_device_set_data(ohmd_device* device, ohmd_data_value type, const void* in) { ohmd_lock_mutex(device->ctx->update_mutex); int ret = ohmd_device_set_data_unp(device, type, in); ohmd_unlock_mutex(device->ctx->update_mutex); return ret; } ohmd_status OHMD_APIENTRY ohmd_device_settings_seti(ohmd_device_settings* settings, ohmd_int_settings key, const int* val) { switch(key){ case OHMD_IDS_AUTOMATIC_UPDATE: settings->automatic_update = val[0] == 0 ? false : true; return OHMD_S_OK; default: return OHMD_S_INVALID_PARAMETER; } } ohmd_device_settings* OHMD_APIENTRY ohmd_device_settings_create(ohmd_context* ctx) { return ohmd_alloc(ctx, sizeof(ohmd_device_settings)); } void OHMD_APIENTRY ohmd_device_settings_destroy(ohmd_device_settings* settings) { free(settings); } void* ohmd_allocfn(ohmd_context* ctx, const char* e_msg, size_t size) { void* ret = calloc(1, size); if(!ret) ohmd_set_error(ctx, "%s", e_msg); return ret; } void ohmd_set_default_device_properties(ohmd_device_properties* props) { props->ipd = 0.061f; props->znear = 0.1f; props->zfar = 1000.0f; } void ohmd_calc_default_proj_matrices(ohmd_device_properties* props) { mat4x4f proj_base; // base projection matrix // Calculate where the lens is on each screen, // and with the given value offset the projection matrix. float screen_center = props->hsize / 4.0f; float lens_shift = screen_center - props->lens_sep / 2.0f; float proj_offset = 4.0f * lens_shift / props->hsize; // Setup the base projection matrix. Each eye mostly have the // same projection matrix with the exception of the offset. omat4x4f_init_perspective(&proj_base, props->fov, props->ratio, props->znear, props->zfar); // Setup the two adjusted projection matricies. Each is setup to deal // with the fact that the lens is not in the center of the screen. // These matrices only change of the hardware changes, so static. mat4x4f translate; omat4x4f_init_translate(&translate, proj_offset, 0, 0); omat4x4f_mult(&translate, &proj_base, &props->proj_left); omat4x4f_init_translate(&translate, -proj_offset, 0, 0); omat4x4f_mult(&translate, &proj_base, &props->proj_right); } OpenHMD-0.2.0/src/openhmdi.h000066400000000000000000000057701277204664200155310ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Internal interface */ #ifndef OPENHMDI_H #define OPENHMDI_H #include "openhmd.h" #include "omath.h" #include "platform.h" #include #include #include #include #define OHMD_MAX_DEVICES 16 #define OHMD_MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b)) #define OHMD_MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b)) #define OHMD_STRINGIFY(_what) #_what typedef struct ohmd_driver ohmd_driver; typedef struct { char driver[OHMD_STR_SIZE]; char vendor[OHMD_STR_SIZE]; char product[OHMD_STR_SIZE]; char path[OHMD_STR_SIZE]; int revision; ohmd_driver* driver_ptr; } ohmd_device_desc; typedef struct { int num_devices; ohmd_device_desc devices[OHMD_MAX_DEVICES]; } ohmd_device_list; struct ohmd_driver { void (*get_device_list)(ohmd_driver* driver, ohmd_device_list* list); ohmd_device* (*open_device)(ohmd_driver* driver, ohmd_device_desc* desc); void (*destroy)(ohmd_driver* driver); ohmd_context* ctx; }; typedef struct { int hres; int vres; float hsize; float vsize; float lens_sep; float lens_vpos; float fov; float ratio; float ipd; float zfar; float znear; int accel_only; //bool-like for setting acceleration only fallback (android driver) mat4x4f proj_left; // adjusted projection matrix for left screen mat4x4f proj_right; // adjusted projection matrix for right screen } ohmd_device_properties; struct ohmd_device_settings { bool automatic_update; }; struct ohmd_device { ohmd_device_properties properties; quatf rotation_correction; vec3f position_correction; int (*getf)(ohmd_device* device, ohmd_float_value type, float* out); int (*setf)(ohmd_device* device, ohmd_float_value type, const float* in); int (*seti)(ohmd_device* device, ohmd_int_value type, const int* in); int (*set_data)(ohmd_device* device, ohmd_data_value type, const void* in); void (*update)(ohmd_device* device); void (*close)(ohmd_device* device); ohmd_context* ctx; ohmd_device_settings settings; int active_device_idx; // index into ohmd_device->active_devices[] quatf rotation; vec3f position; }; struct ohmd_context { ohmd_driver* drivers[16]; int num_drivers; ohmd_device_list list; ohmd_device* active_devices[256]; int num_active_devices; ohmd_thread* update_thread; ohmd_mutex* update_mutex; bool update_request_quit; char error_msg[OHMD_STR_SIZE]; }; // helper functions void ohmd_set_default_device_properties(ohmd_device_properties* props); void ohmd_calc_default_proj_matrices(ohmd_device_properties* props); // drivers ohmd_driver* ohmd_create_dummy_drv(ohmd_context* ctx); ohmd_driver* ohmd_create_oculus_rift_drv(ohmd_context* ctx); ohmd_driver* ohmd_create_external_drv(ohmd_context* ctx); ohmd_driver* ohmd_create_android_drv(ohmd_context* ctx); #include "log.h" #include "omath.h" #include "fusion.h" #endif OpenHMD-0.2.0/src/platform-posix.c000066400000000000000000000050041277204664200166730ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Platform Specific Functions, Unix/Posix Implementation */ #if defined(__unix__) || defined(__unix) || defined(__APPLE__) || defined(__MACH__) #ifdef __CYGWIN__ #define CLOCK_MONOTONIC (clockid_t)4 #endif #define _POSIX_C_SOURCE 199309L #include #include #include #include #include "platform.h" #include "openhmdi.h" // Use clock_gettime if the system implements posix realtime timers #ifndef CLOCK_MONOTONIC double ohmd_get_tick() { struct timeval now; gettimeofday(&now, NULL); return (double)now.tv_sec * 1.0 + (double)now.tv_usec / 1000000.0; } #else double ohmd_get_tick() { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return (double)now.tv_sec * 1.0 + (double)now.tv_nsec / 1000000000.0; } #endif void ohmd_sleep(double seconds) { struct timespec sleepfor; sleepfor.tv_sec = (time_t)seconds; sleepfor.tv_nsec = (long)((seconds - sleepfor.tv_sec) * 1000000000.0); nanosleep(&sleepfor, NULL); } // threads struct ohmd_thread { pthread_t thread; unsigned int (*routine)(void* arg); void* arg; }; static void* pthread_wrapper(void* arg) { ohmd_thread* my_thread = (ohmd_thread*)arg; my_thread->routine(my_thread->arg); return NULL; } ohmd_thread* ohmd_create_thread(ohmd_context* ctx, unsigned int (*routine)(void* arg), void* arg) { ohmd_thread* thread = ohmd_alloc(ctx, sizeof(ohmd_thread)); if(thread == NULL) return NULL; thread->arg = arg; thread->routine = routine; int ret = pthread_create(&thread->thread, NULL, pthread_wrapper, thread); if(ret != 0){ free(thread); thread = NULL; } return thread; } ohmd_mutex* ohmd_create_mutex(ohmd_context* ctx) { pthread_mutex_t* mutex = ohmd_alloc(ctx, sizeof(pthread_mutex_t)); if(mutex == NULL) return NULL; int ret = pthread_mutex_init(mutex, NULL); if(ret != 0){ free(mutex); mutex = NULL; } return (ohmd_mutex*)mutex; } void ohmd_destroy_thread(ohmd_thread* thread) { pthread_join(thread->thread, NULL); free(thread); } void ohmd_destroy_mutex(ohmd_mutex* mutex) { pthread_mutex_destroy((pthread_mutex_t*)mutex); free(mutex); } void ohmd_lock_mutex(ohmd_mutex* mutex) { if(mutex) pthread_mutex_lock((pthread_mutex_t*)mutex); } void ohmd_unlock_mutex(ohmd_mutex* mutex) { if(mutex) pthread_mutex_unlock((pthread_mutex_t*)mutex); } #endif OpenHMD-0.2.0/src/platform-win32.c000066400000000000000000000037271277204664200165050ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Platform Specific Functions, Win32 Implementation */ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define WIN32_EXTRA_LEAN #include #include "platform.h" #include "openhmdi.h" double ohmd_get_tick() { double high, low; FILETIME filetime; GetSystemTimeAsFileTime(&filetime); high = filetime.dwHighDateTime; low = filetime.dwLowDateTime; return (high * 4294967296.0 + low) / 10000000; } // TODO higher resolution void ohmd_sleep(double seconds) { Sleep((DWORD)(seconds * 1000)); } // threads struct ohmd_thread { HANDLE handle; void* arg; unsigned int (*routine)(void* arg); }; struct ohmd_mutex { HANDLE handle; }; DWORD __stdcall ohmd_thread_wrapper(void* t) { ohmd_thread* thread = (ohmd_thread*)t; return thread->routine(thread->arg); } ohmd_thread* ohmd_create_thread(ohmd_context* ctx, unsigned int (*routine)(void* arg), void* arg) { ohmd_thread* thread = ohmd_alloc(ctx, sizeof(ohmd_thread)); if(!thread) return NULL; thread->routine = routine; thread->arg = arg; thread->handle = CreateThread(NULL, 0, ohmd_thread_wrapper, thread, 0, NULL); return thread; } void ohmd_destroy_thread(ohmd_thread* thread) { ohmd_sleep(3); WaitForSingleObject(thread->handle, INFINITE); CloseHandle(thread->handle); free(thread); } ohmd_mutex* ohmd_create_mutex(ohmd_context* ctx) { ohmd_mutex* mutex = ohmd_alloc(ctx, sizeof(ohmd_mutex)); if(!mutex) return NULL; mutex->handle = CreateMutex(NULL, FALSE, NULL); return mutex; } void ohmd_destroy_mutex(ohmd_mutex* mutex) { CloseHandle(mutex->handle); free(mutex); } void ohmd_lock_mutex(ohmd_mutex* mutex) { if(mutex) WaitForSingleObject(mutex->handle, INFINITE); } void ohmd_unlock_mutex(ohmd_mutex* mutex) { if(mutex) ReleaseMutex(mutex->handle); } #endif OpenHMD-0.2.0/src/platform.h000066400000000000000000000014711277204664200155440ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Internal Interface for Platform Specific Functions */ #ifndef PLATFORM_H #define PLATFORM_H #include "openhmd.h" double ohmd_get_tick(); void ohmd_sleep(double seconds); typedef struct ohmd_thread ohmd_thread; typedef struct ohmd_mutex ohmd_mutex; ohmd_mutex* ohmd_create_mutex(ohmd_context* ctx); void ohmd_destroy_mutex(ohmd_mutex* mutex); void ohmd_lock_mutex(ohmd_mutex* mutex); void ohmd_unlock_mutex(ohmd_mutex* mutex); ohmd_thread* ohmd_create_thread(ohmd_context* ctx, unsigned int (*routine)(void* arg), void* arg); void ohmd_destroy_thread(ohmd_thread* thread); #endif OpenHMD-0.2.0/tests/000077500000000000000000000000001277204664200141175ustar00rootroot00000000000000OpenHMD-0.2.0/tests/Makefile.am000066400000000000000000000000241277204664200161470ustar00rootroot00000000000000SUBDIRS = unittests OpenHMD-0.2.0/tests/unittests/000077500000000000000000000000001277204664200161615ustar00rootroot00000000000000OpenHMD-0.2.0/tests/unittests/Makefile.am000066400000000000000000000004041277204664200202130ustar00rootroot00000000000000bin_PROGRAMS = unittests AM_CPPFLAGS = -Wall -Werror -I$(top_srcdir)/include -I$(top_srcdir)/src -DOHMD_STATIC unittests_SOURCES = main.c quat.c vec.c highlevel.c unittests_LDADD = $(top_builddir)/src/libopenhmd.la -lm unittests_LDFLAGS = -static-libtool-libs OpenHMD-0.2.0/tests/unittests/highlevel.c000066400000000000000000000027531277204664200203030ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Unit Tests - High-level functions */ #include "tests.h" #include "openhmd.h" void test_highlevel_open_close_device() { ohmd_context* ctx = ohmd_ctx_create(); TAssert(ctx); // Probe for devices int num_devices = ohmd_ctx_probe(ctx); TAssert(num_devices > 0); // Open dummy device (num_devices - 1) ohmd_device* hmd = ohmd_list_open_device(ctx, num_devices - 1); TAssert(hmd); // Close the device int ret = ohmd_close_device(hmd); TAssert(ret == 0); ohmd_ctx_destroy(ctx); } void test_highlevel_open_close_many_devices() { ohmd_context* ctx = ohmd_ctx_create(); TAssert(ctx); // Probe for devices int num_devices = ohmd_ctx_probe(ctx); TAssert(num_devices > 0); ohmd_device* hmds[16]; for(int i = 0; i < 8; i++){ // Open dummy device (num_devices - 1) hmds[i] = ohmd_list_open_device(ctx, num_devices - 1); TAssert(hmds[i]); } for(int i = 4; i < 8; i++){ // Close the device int ret = ohmd_close_device(hmds[i]); TAssert(ret == 0); } for(int i = 4; i < 16; i++){ // Open dummy device (num_devices - 1) hmds[i] = ohmd_list_open_device(ctx, num_devices - 1); TAssert(hmds[i]); } for(int i = 0; i < 16; i++){ // Close the device int ret = ohmd_close_device(hmds[i]); TAssert(ret == 0); } ohmd_ctx_destroy(ctx); } OpenHMD-0.2.0/tests/unittests/main.c000066400000000000000000000017411277204664200172540ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Unit Tests - Main */ #include #include "tests.h" bool float_eq(float a, float b, float t) { return fabsf(a - b) < t; } #define Test(_t) printf(" "#_t); _t(); printf("%*sok\n", 50 - (int)strlen(#_t), ""); int main() { printf("vec3f tests\n"); Test(test_ovec3f_normalize_me); Test(test_ovec3f_get_length); Test(test_ovec3f_get_angle); Test(test_ovec3f_get_dot); printf("\n"); printf("quatf tests\n"); Test(test_oquatf_init_axis); Test(test_oquatf_get_rotated); Test(test_oquatf_get_dot); Test(test_oquatf_inverse); Test(test_oquatf_diff); printf("\n"); printf("high level tests\n"); Test(test_highlevel_open_close_device); Test(test_highlevel_open_close_many_devices); printf("\n"); printf("all a-ok\n"); return 0; } OpenHMD-0.2.0/tests/unittests/quat.c000066400000000000000000000064441277204664200173070ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Unit Tests - Quaternion Tests */ #include "tests.h" static const float t = 0.001; bool quatf_eq(quatf q1, quatf q2, float t) { for(int i = 0; i < 4; i++) if(!float_eq(q1.arr[i], q2.arr[i], t)){ printf("\nquat.arr[%d] == %f, expected %f\n", i, q1.arr[i], q2.arr[i]); return false; } return true; } typedef struct { vec3f v; float f; quatf q; } vec_float_quat; void test_oquatf_init_axis() { //void oquatf_init_axis(quatf* me, const vec3f* vec, float angle); vec_float_quat list[] = { { {{0, 0, 0}}, 0, {{0, 0, 0, 1}}}, { {{5, 12, 3}}, 1, {{0.1796723168488794, 0.43121356043731063, 0.10780339010932766, 0.8775825618903728}}}, { {{-2, -3, 3}}, 1, {{-0.2044277365391809, -0.30664160480877134, 0.30664160480877134, 0.8775825618903728}} }, { {{100, -3, 3}}, -300, {{0.7142339081165469, -0.021427017243496407, 0.021427017243496407, 0.6992508064783751}} }, }; int sz = sizeof(vec_float_quat); for(int i = 0; i < sizeof(list) / sz; i++){ quatf q; oquatf_init_axis(&q, &list[i].v, list[i].f); //printf("%f %f %f %f\n", q.x, q.y, q.z, q.w); TAssert(quatf_eq(q, list[i].q, t)); } } typedef struct { quatf q; vec3f v1, v2; } quat_vec2; void test_oquatf_get_rotated() { // void oquatf_get_rotated(const quatf* me, const vec3f* vec, vec3f* out_vec); quat_vec2 list[] = { { {{0, 0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}} }, { {{0, 0, 0, 0}}, {{1, 2, 3}}, {{0, 0, 0}} }, { {{0, 0, 0, 1}}, {{1, 2, 3}}, {{1, 2, 3}} }, { {{.4, .2, .1, 1}}, {{2, 1, 0}}, {{2.18, 1.59, 0.2}} }, { {{.4, .2, .1, -1}}, {{2, 1, 0}}, {{2.58, 0.79, 0.2}} }, }; int sz = sizeof(quat_vec2); for(int i = 0; i < sizeof(list) / sz; i++){ vec3f vec; oquatf_get_rotated(&list[i].q, &list[i].v1, &vec); TAssert(vec3f_eq(vec, list[i].v2, t)); } } // TODO test_oquatf_mult void test_oquatf_mult() { } // TODO test_oquatf_normalize void test_oquatf_normalize() { } // TODO test_oquatf_get_length void test_oquatf_get_length() { } // TODO test_oquatf_get_mat4x4 void test_oquatf_get_mat4x4() { } typedef struct { quatf q1, q2; float f; } quat2_float; void test_oquatf_get_dot() { // TODO add more test cases quat2_float list[] = { { {{1, 2, 3, 1}}, {{4, 3, 2, .5}}, 16.5 }, }; int sz = sizeof(quat2_float); for(int i = 0; i < sizeof(list) / sz; i++){ TAssert(float_eq(oquatf_get_dot(&list[i].q1, &list[i].q2), list[i].f, t)); } } typedef struct { quatf q1, q2; } quat2; void test_oquatf_inverse() { // TODO add more test cases quat2 list[] = { { {{1, 2, 3, 1}}, {{-0.06666666666666667, -0.13333333333333333, -0.2, 0.06666666666666667}} }, }; int sz = sizeof(quat2); for(int i = 0; i < sizeof(list) / sz; i++){ oquatf_inverse(&list[i].q1); TAssert(quatf_eq(list[i].q1, list[i].q2, t)); } } typedef struct { quatf q1, q2, q3; } quat3; void test_oquatf_diff() { // TODO add more test cases quat3 list[] = { { {{1, 2, 3, 1}}, {{5, 3, 2, .1}}, {{0.660000, -0.680000, 0.580000, 1.140000}} }, }; int sz = sizeof(quat3); for(int i = 0; i < sizeof(list) / sz; i++){ quatf q; oquatf_diff(&list[i].q1, &list[i].q2, &q); TAssert(quatf_eq(q, list[i].q3, t)); } } OpenHMD-0.2.0/tests/unittests/tests.h000066400000000000000000000021721277204664200174760ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Unit Tests - Internal Interface */ #ifndef TESTS_H #define TESTS_H #include #include #include #include "openhmdi.h" #define TAssert(_v) if(!(_v)){ printf("\ntest failed: %s @ %s:%d\n", __func__, __FILE__, __LINE__); exit(1); } bool float_eq(float a, float b, float t); bool vec3f_eq(vec3f v1, vec3f v2, float t); // vec3f tests void test_ovec3f_normalize_me(); void test_ovec3f_get_length(); void test_ovec3f_get_angle(); void test_ovec3f_get_dot(); // quatf tests void test_oquatf_init_axis(); void test_oquatf_get_rotated(); void test_oquatf_mult(); void test_oquatf_mult_me(); void test_oquatf_normalize(); void test_oquatf_get_length(); void test_oquatf_get_dot(); void test_oquatf_inverse(); void test_oquatf_diff(); void test_oquatf_get_mat4x4(); // high-level tests void test_highlevel_open_close_device(); void test_highlevel_open_close_many_devices(); #endif OpenHMD-0.2.0/tests/unittests/vec.c000066400000000000000000000045561277204664200171140ustar00rootroot00000000000000/* * OpenHMD - Free and Open Source API and drivers for immersive technology. * Copyright (C) 2013 Fredrik Hultin. * Copyright (C) 2013 Jakob Bornecrantz. * Distributed under the Boost 1.0 licence, see LICENSE for full text. */ /* Unit Tests - Vector3f Tests */ #include "tests.h" bool vec3f_eq(vec3f v1, vec3f v2, float t) { for(int i = 0; i < 3; i++) if(!float_eq(v1.arr[i], v2.arr[i], t)){ printf("\nvec.arr[%d] == %f, expected %f\n", i, v1.arr[i], v2.arr[i]); return false; } return true; } void test_ovec3f_normalize_me() { vec3f v[][2] = { { {{1, 0, 0}}, {{1, 0, 0}} }, { {{1, 2, 3}}, {{0.267261241912424, 0.534522483824849, 0.801783725737273}} }, { {{-7, 13, 22}}, {{-0.264197974633739, 0.490653381462658, 0.830336491706037}} }, { {{.1, .1, .1}}, {{0.577350269189626, 0.577350269189626, 0.577350269189626}} }, { {{0, 0, 0}}, {{0, 0, 0}} }, }; int sz = sizeof(vec3f) * 2; float t = 0.001; for(int i = 0; i < sizeof(v) / sz; i++){ vec3f norm = v[i][0]; ovec3f_normalize_me(&norm); TAssert(vec3f_eq(norm, v[i][1], t)); } } typedef struct { vec3f vec; float f; } vec_float; void test_ovec3f_get_length() { vec_float vf[] = { { {{0, 0, 0}}, 0}, { {{1, 0, 0}}, 1}, { {{1, 2, 0}}, 2.23606797749979}, { {{1, -2, 0}}, 2.23606797749979}, { {{1, 2, 3}}, 3.7416573867739413}, { {{-1, -2, -3}}, 3.7416573867739413}, }; int sz = sizeof(vec_float); float t = 0.001; for(int i = 0; i < sizeof(vf) / sz; i++){ TAssert(float_eq(ovec3f_get_length(&vf[i].vec), vf[i].f, t)); } } typedef struct { vec3f v1, v2; float f; } vec2_float; void test_ovec3f_get_angle() { vec2_float vf[] = { { {{0, 0, 0}}, {{0, 0, 0}}, 0}, { {{1, 0, 0}}, {{0, 0, 0}}, 0}, { {{2, 4, 3}}, {{1, 2, 3}}, 0.33940126397005316}, { {{2, 4, 3}}, {{-1, 2, 3}}, 0.7311043352203973}, }; int sz = sizeof(vec2_float); float t = 0.001; for(int i = 0; i < sizeof(vf) / sz; i++){ TAssert(float_eq(ovec3f_get_angle(&vf[i].v1, &vf[i].v2), vf[i].f, t)); } } //float ovec3f_get_dot(const vec3f* me, const vec3f* vec); void test_ovec3f_get_dot() { vec2_float vf[] = { { {{0, 0, 0}}, {{0, 0, 0}}, 0}, { {{1, 2, 3}}, {{-.30, 2, 25}}, 78.7}, { {{-1, -10000000, 3}}, {{-.30, 2, 25}}, -19999924.7}, }; int sz = sizeof(vec2_float); float t = 0.001; for(int i = 0; i < sizeof(vf) / sz; i++){ TAssert(float_eq(ovec3f_get_dot(&vf[i].v1, &vf[i].v2), vf[i].f, t)); } }