packages feed

oculus-0.1.0.0: src/Rift.hs

-- | Access Rift headtracking data and constants related to screen, distortion parameters, etc..

module Rift 
       ( RiftHandle
       , initRift, clearRift
       , orientation, orientationQ
       , predictedOrientation, predictedOrientationQ
       , acceleration
         
       , ProductInfo(..), productInfo
       , ScreenInfo(..), screenInfo
       , DistortionKInfo(..), distortionKInfoToVec4, distortionKInfo
       , ChromaAbCorrectionInfo(..), chromaAbCorrectionInfoToVec4, chromaAbCorrectionInfo
       ) where

import Control.Applicative

import Control.Monad.IO.Class
import Control.Monad.Trans.Either

import Control.Monad.Cont

import Data.Vect.Floating
import Data.Vect.Floating.Util.Quaternion

import Foreign.C
import Foreign.Ptr
import Foreign.Marshal.Alloc
import Foreign.Storable

foreign import ccall unsafe "initRift" c_initRift :: IO Bool
foreign import ccall unsafe "clearRift" c_clearRift :: IO ()

foreign import ccall unsafe "readOrientation" c_readOrientation :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()
foreign import ccall unsafe "readOrientationQ" c_readOrientationQ :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()
foreign import ccall unsafe "readPredictedOrientation" c_readPredictedOrientation :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()
foreign import ccall unsafe "readPredictedOrientationQ" c_readPredictedOrientationQ :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()
foreign import ccall unsafe "readAcceleration" c_readAcceleration :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()

foreign import ccall unsafe "populateProductInfo" c_populateProductInfo :: CString -> CString -> CString -> Ptr CUInt -> IO ()
foreign import ccall unsafe "populateScreenInfo" c_populateScreenInfo :: Ptr CUInt -> Ptr CUInt -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()
foreign import ccall unsafe "populateDistortionKInfo" c_populateDistortionKInfo :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()
foreign import ccall unsafe "populateChromaAbCorrectionInfo" c_populateChromaAbCorrectionInfo :: Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> Ptr CFloat -> IO ()

-- | Handle to rift, constructor intentionally hidden, used to enforce that `initRift` was called before other Rift related functions.
data RiftHandle = R

-- | Error code for why call to `initRift` might have failed, not very descriptive yet.
data ErrorCode = InitializationFailed
               deriving Show

-- | Initializes the rift, returns ErrorCode if failure, returns a RiftHandle if success. Not safe to call twice without a call to `clearRift`.
initRift :: EitherT ErrorCode IO RiftHandle
initRift = do
  b <- liftIO c_initRift
  case b of
    True -> return R
    False -> left InitializationFailed
    
-- | Cleans up handle, frees memory, detaches from Rift, makes next call to `initRift` safe.
clearRift :: RiftHandle -> IO ()    
clearRift _ = c_clearRift

-- | Returns orientation of Rift in degrees starting at 0, from -pi to pi. 
-- Deconstructs to `Vec3` roll pitch yaw
orientation :: RiftHandle -> IO (Vec3 Float)
orientation _ = (`runContT` id) $ do
  roll <- ContT alloca
  pitch <- ContT alloca
  yaw <- ContT alloca
  liftIO $ do
    c_readOrientation roll pitch yaw
    return $ Vec3 
      <$> (fmap realToFrac . peek) roll 
      <*> (fmap realToFrac . peek) pitch 
      <*> (fmap realToFrac . peek) yaw

-- | Returns orientation of Rift in the UnitQuaternion, can apply as rotation with functions from `Data.Vect.Floating.Util.Quaternion` module in the <http://hackage.haskell.org/package/vect-floating> package.
orientationQ :: RiftHandle -> IO (UnitQuaternion Float)
orientationQ _ = (`runContT` id) $ do
  x <- ContT alloca
  y <- ContT alloca
  z <- ContT alloca
  w <- ContT alloca
  liftIO $ do
    c_readOrientationQ x y z w
    return . fmap unsafeToU $ Vec4 
      <$> (fmap realToFrac . peek) x 
      <*> (fmap realToFrac . peek) y 
      <*> (fmap realToFrac . peek) z 
      <*> (fmap realToFrac . peek) w

-- | Same as `orientation`, however, factors in velocity from Rift to give a predicted orientation with a lookahead defaulted to 0.03 seconds.
predictedOrientation :: RiftHandle -> IO (Vec3 Float)
predictedOrientation _ = (`runContT` id) $ do
  roll <- ContT alloca
  pitch <- ContT alloca
  yaw <- ContT alloca
  liftIO $ do
    c_readPredictedOrientation roll pitch yaw
    return $ Vec3 
      <$> (fmap realToFrac . peek) roll 
      <*> (fmap realToFrac . peek) pitch 
      <*> (fmap realToFrac . peek) yaw

-- | Same as 'orientationQ`, however, factors in velocity from Rift to give a predicted orientation with a lookahead defaulted to 0.03 seconds.
predictedOrientationQ :: RiftHandle -> IO (UnitQuaternion Float)
predictedOrientationQ _ = (`runContT` id) $ do
  x <- ContT alloca
  y <- ContT alloca
  z <- ContT alloca
  w <- ContT alloca
  liftIO $ do
    c_readPredictedOrientationQ x y z w
    return . fmap unsafeToU $ Vec4 
      <$> (fmap realToFrac . peek) x 
      <*> (fmap realToFrac . peek) y 
      <*> (fmap realToFrac . peek) z 
      <*> (fmap realToFrac . peek) w

-- | Returns last absolute acceleration reading, in m/s^2. Deconstructs to `Vec3` x y z.
acceleration :: RiftHandle -> IO (Vec3 Float)
acceleration _ = (`runContT` id) $ do
  x <- ContT alloca
  y <- ContT alloca
  z <- ContT alloca
  liftIO $ do
    c_readAcceleration x y z
    return $ Vec3 
      <$> (fmap realToFrac . peek) x 
      <*> (fmap realToFrac . peek) y 
      <*> (fmap realToFrac . peek) z
    
{- Constants from Oculus Rift -}

-- | Info about product.
data ProductInfo = 
  ProductInfo 
  { displayDeviceName :: String
  , productName :: String
  , manufacturer :: String
  , version :: Int
  } deriving (Show)
             
-- | Grabs product info from Rift.
productInfo :: RiftHandle -> IO ProductInfo
productInfo _ = (`runContT` id) $ do
  displayDeviceName' <- ContT $ withCString (replicate 33 '0')
  productName' <- ContT $ withCString (replicate 33 '0')
  manufacturer' <- ContT $ withCString (replicate 33 '0')
  version' <- ContT alloca
  liftIO $ do
    c_populateProductInfo displayDeviceName' productName' manufacturer' version'
    return $ ProductInfo
      <$> peekCString displayDeviceName' 
      <*> peekCString productName' 
      <*> peekCString manufacturer' 
      <*> (fmap fromIntegral . peek) version'
    
-- | Info about screen, all distances are in meters.
data ScreenInfo =
  ScreenInfo
  { hResolution :: Int
  , vResolution :: Int
  , hScreenSize :: Float
  , vScreenSize :: Float
  , vScreenCenter :: Float
  , eyeToScreenDistance :: Float
  , lensSeparationDistance :: Float
  , interpupillaryDistance :: Float
  } deriving (Show)
             
-- | Grabs screen info from Rift.
screenInfo :: RiftHandle -> IO ScreenInfo
screenInfo _ = (`runContT` id) $ do
  hResolution' <- ContT alloca
  vResolution' <- ContT alloca
  hScreenSize' <- ContT alloca
  vScreenSize' <- ContT alloca
  vScreenCenter' <- ContT alloca
  eyeToScreenDistance' <- ContT alloca
  lensSeparationDistance' <- ContT alloca
  interpupillaryDistance' <- ContT alloca
  liftIO $ do
    c_populateScreenInfo
      hResolution'
      vResolution'
      hScreenSize'
      vScreenSize'
      vScreenCenter'
      eyeToScreenDistance'
      lensSeparationDistance'
      interpupillaryDistance'
    return $ ScreenInfo
      <$> (fmap fromIntegral . peek) hResolution'
      <*> (fmap fromIntegral . peek) vResolution'
      <*> (fmap realToFrac . peek) hScreenSize'
      <*> (fmap realToFrac . peek) vScreenSize'
      <*> (fmap realToFrac . peek) vScreenCenter'
      <*> (fmap realToFrac . peek) eyeToScreenDistance'
      <*> (fmap realToFrac . peek) lensSeparationDistance'
      <*> (fmap realToFrac . peek) interpupillaryDistance'
    
-- | Information about distortion correction parameters necessary to correct lenses in Rift.
data DistortionKInfo =
  DistortionKInfo 
  { distortionK0 :: Float
  , distortionK1 :: Float
  , distortionK2 :: Float
  , distortionK3 :: Float
  } deriving (Show)
             
-- | Converts distortion correction parameters to a 4D vector.
distortionKInfoToVec4 :: DistortionKInfo -> Vec4 Float
distortionKInfoToVec4 (DistortionKInfo c0 c1 c2 c3) = Vec4 c0 c1 c2 c3    
             
-- | Grabs distortion correction parameters from Rift.
distortionKInfo :: RiftHandle -> IO DistortionKInfo
distortionKInfo _ = (`runContT` id) $ do
  distortionK0' <- ContT alloca
  distortionK1' <- ContT alloca
  distortionK2' <- ContT alloca
  distortionK3' <- ContT alloca
  liftIO $ do
    c_populateDistortionKInfo 
      distortionK0'
      distortionK1'
      distortionK2'
      distortionK3'
    return $ DistortionKInfo
      <$> (fmap realToFrac . peek) distortionK0'
      <*> (fmap realToFrac . peek) distortionK1'
      <*> (fmap realToFrac . peek) distortionK2'
      <*> (fmap realToFrac . peek) distortionK3'
    
-- | Information about chromatic aberration correction parameters necessary to correct color changes due to lenses in Rift.
data ChromaAbCorrectionInfo =
  ChromaAbCorrectionInfo 
  { chromaAbCorrection0 :: Float
  , chromaAbCorrection1 :: Float
  , chromaAbCorrection2 :: Float
  , chromaAbCorrection3 :: Float
  } deriving (Show)
             
-- | Converts chromatic aberration correction parameters to a 4D vector.
chromaAbCorrectionInfoToVec4 :: ChromaAbCorrectionInfo -> Vec4 Float
chromaAbCorrectionInfoToVec4 (ChromaAbCorrectionInfo c0 c1 c2 c3) = Vec4 c0 c1 c2 c3    
             
-- | Grabs chromatic aberration correction parameters from Rift.
chromaAbCorrectionInfo :: RiftHandle -> IO ChromaAbCorrectionInfo
chromaAbCorrectionInfo _ = (`runContT` id) $ do
  chromaAbCorrection0' <- ContT alloca
  chromaAbCorrection1' <- ContT alloca
  chromaAbCorrection2' <- ContT alloca
  chromaAbCorrection3' <- ContT alloca
  liftIO $ do
    c_populateChromaAbCorrectionInfo 
      chromaAbCorrection0'
      chromaAbCorrection1'
      chromaAbCorrection2'
      chromaAbCorrection3'
    return $ ChromaAbCorrectionInfo
      <$> (fmap realToFrac . peek) chromaAbCorrection0'
      <*> (fmap realToFrac . peek) chromaAbCorrection1'
      <*> (fmap realToFrac . peek) chromaAbCorrection2'
      <*> (fmap realToFrac . peek) chromaAbCorrection3'