module Graphics.Rendering.Handa.Viewer (
ViewerParameters(..)
, viewerGeometry
, displayAspectRatio
, displayThrowRatio
, fieldOfView
, phoneViewer
, laptopViewer
, desktopViewer
, projectorViewer
, reshape
, loadViewer
, dlpViewerDisplay
) where
import Data.AdditiveGroup (AdditiveGroup)
import Data.Aeson (FromJSON)
import Data.Binary (Binary(..))
import Data.Data (Data)
import Data.Default (Default, def)
import Foreign.Storable (Storable)
import GHC.Generics (Generic)
import Graphics.Rendering.DLP (DlpEncoding, DlpEye(..))
import Graphics.Rendering.DLP.Callbacks (DlpDisplay(..))
import Graphics.Rendering.Handa.Projection (Screen(..), aspectRatio, projection, throwRatio)
import Graphics.Rendering.Handa.Util (degree)
import Graphics.Rendering.OpenGL (GLdouble, MatrixComponent, MatrixMode(..), Position(..), Vector3(..), Vertex3(..), ($=!), loadIdentity, lookAt, matrixMode, scale, viewport)
import Graphics.Rendering.OpenGL.GL.Tensor.Instances ()
import Graphics.UI.GLUT (DisplayCallback, ReshapeCallback)
data ViewerParameters a =
ViewerParameters
{
screen :: Screen a
, nearPlane :: a
, farPlane :: a
, eyePosition :: Vertex3 a
, eyeSeparation :: Vector3 a
, eyeUpward :: Vector3 a
, sceneCenter :: Vertex3 a
, sceneScale :: Vector3 a
}
deriving (Binary, Data, Eq, FromJSON, Generic, Read, Show)
instance Functor ViewerParameters where
fmap f ViewerParameters{..} =
ViewerParameters
{
screen = fmap f screen
, nearPlane = f nearPlane
, farPlane = f farPlane
, eyePosition = fmap f eyePosition
, eyeSeparation = fmap f eyeSeparation
, eyeUpward = fmap f eyeUpward
, sceneCenter = fmap f sceneCenter
, sceneScale = fmap f sceneScale
}
instance (Fractional a, Storable a) => Default (ViewerParameters a) where
def =
ViewerParameters
{
screen =
Screen
{
lowerLeft = Vertex3 (0.5) (0.5) 0
, lowerRight = Vertex3 0.5 (0.5) 0
, upperLeft = Vertex3 (0.5) 0.5 0
}
, nearPlane = 0.1
, farPlane = 100
, eyePosition = Vertex3 0 0 1
, eyeSeparation = Vector3 0.2 0 0
, eyeUpward = Vector3 0 1 0
, sceneCenter = Vertex3 0 0 0
, sceneScale = Vector3 1 1 1
}
viewerGeometry :: (Fractional a, Storable a)
=> a
-> a
-> a
-> ViewerParameters a
viewerGeometry width height throw =
def
{
screen =
Screen
{
lowerLeft = Vertex3 ( 1 / 2) ( height / width / 2) 0
, lowerRight = Vertex3 ( 1 / 2) ( height / width / 2) 0
, upperLeft = Vertex3 ( 1 / 2) ( height / width / 2) 0
}
, eyePosition = Vertex3 0 0 (throw / width)
, sceneScale = Vector3 1 (height / width) 1
}
phoneViewer :: (Fractional a, Storable a) => ViewerParameters a
phoneViewer = viewerGeometry 5.27 2.80 12
laptopViewer :: (Fractional a, Storable a) => ViewerParameters a
laptopViewer = viewerGeometry 13.625 7.875 24
desktopViewer :: (Fractional a, Storable a) => ViewerParameters a
desktopViewer = viewerGeometry 20.75 11.625 32
projectorViewer :: (Fractional a, Storable a) => ViewerParameters a
projectorViewer = viewerGeometry 1.6 1.0 (1.5 * 1.6)
displayAspectRatio :: (AdditiveGroup a, RealFloat a, Storable a)
=> ViewerParameters a
-> a
displayAspectRatio ViewerParameters{..} = aspectRatio screen
displayThrowRatio :: (AdditiveGroup a, RealFloat a, Storable a)
=> ViewerParameters a
-> a
displayThrowRatio ViewerParameters{..} = throwRatio screen eyePosition
fieldOfView :: (AdditiveGroup a, RealFloat a, Storable a)
=> ViewerParameters a
-> a
fieldOfView ViewerParameters{..} = 2 * atan2 0.5 (throwRatio screen eyePosition) * degree
reshape :: (AdditiveGroup a, MatrixComponent a, RealFloat a, Storable a)
=> ViewerParameters a
-> ReshapeCallback
reshape ViewerParameters{..} wh =
do
viewport $=! (Position 0 0, wh)
matrixMode $=! Projection
loadIdentity
projection screen eyePosition nearPlane farPlane
matrixMode $=! Modelview 0
loadViewer :: (RealFloat a, Storable a)
=> ViewerParameters a
-> DlpEye
-> IO ()
loadViewer ViewerParameters{..} eye =
do
let
offset =
case eye of
LeftDlp -> 0.5
RightDlp -> 0.5
Vertex3 xEye yEye zEye = eyePosition
Vector3 dxEye dyEye dzEye = eyeSeparation
Vector3 sx sy sz = realToFrac <$> sceneScale :: Vector3 GLdouble
loadIdentity
scale sx sy sz
lookAt
(realToFrac <$> Vertex3 (xEye + offset * dxEye) (yEye + offset * dyEye) (zEye + offset * dzEye))
(realToFrac <$> sceneCenter)
(realToFrac <$> eyeUpward)
dlpViewerDisplay :: (RealFloat a, Storable a)
=> DlpEncoding
-> ViewerParameters a
-> DisplayCallback
-> DlpDisplay
dlpViewerDisplay encoding viewerParameters display =
def
{
dlpEncoding = encoding
, doDisplay = \eye -> loadViewer viewerParameters eye >> display
}