handa-opengl-0.1.6.1: src/Graphics/Rendering/Handa/Viewer.hs
{-|
Module : Graphics.Rendering.Handa.Viewer
Copyright : (c) 2015 Brian W Bush
License : MIT
Maintainer : Brian W Bush <consult@brianwbush.info>
Stability : Stable
Portability : Portable
Functions for managing perspectives and frusta.
-}
{-# LANGUAGE RecordWildCards #-}
module Graphics.Rendering.Handa.Viewer (
-- * Viewer Geometry
ViewerParameters(..)
, viewerGeometry
, fieldOfView
-- * Typical Devices
, phoneViewer
, laptopViewer
, desktopViewer
, projectorViewer
-- * Callbacks and Rendering
, reshape
, loadViewer
, dlpViewerDisplay
) where
import Data.Default (Default, def)
import Graphics.Rendering.DLP (DlpEncoding, DlpEye(..))
import Graphics.Rendering.DLP.Callbacks (DlpDisplay(..))
import Graphics.Rendering.Handa.Util (degree)
import Graphics.Rendering.OpenGL (GLdouble, MatrixMode(..), Position(..), Size(..), Vector3(..), Vertex3(..), ($=!), loadIdentity, lookAt, matrixMode, perspective, viewport)
import Graphics.UI.GLUT (DisplayCallback, ReshapeCallback)
-- | Paramaters specifying a viewer, including the frustum of the view.
data ViewerParameters =
ViewerParameters
{
displayAspectRatio :: GLdouble -- ^ The aspect ratio of the screen or display (i.e., the width divided by the height).
, displayThrowRatio :: GLdouble -- ^ The throw ratio of the screen or display (i.e., the width divided by the distance).
, distanceNearPlane :: GLdouble -- ^ The distance to the near plane of the frustum.
, distanceFarPlane :: GLdouble -- ^ The distance to the far plane of the frustum.
, eyePosition :: Vertex3 GLdouble -- ^ The position of the eyes.
, eyeSeparation :: Vector3 GLdouble -- ^ The separation between the eyes.
, eyeUpward :: Vector3 GLdouble -- ^ The upward direction.
, sceneCenter :: Vertex3 GLdouble -- ^ The center of the scene.
}
deriving (Eq, Read, Show)
instance Default ViewerParameters where
def =
ViewerParameters
{
displayAspectRatio = 1
, displayThrowRatio = 1
, distanceNearPlane = 0.5
, distanceFarPlane = 4.5
, eyePosition = Vertex3 0 0 2
, eyeSeparation = Vector3 0.2 0 0
, eyeUpward = Vector3 0 1 0
, sceneCenter = Vertex3 0 0 0
}
-- | Construct viewer geometry from physical geometry.
viewerGeometry :: GLdouble -- ^ The width of the screen or display.
-> GLdouble -- ^ The height of the screen or display.
-> GLdouble -- ^ The distance from the eyes to the screen or display.
-> ViewerParameters -- ^ The corresponding viewer parameters.
viewerGeometry width height throw =
def
{
displayAspectRatio = width / height
, displayThrowRatio = throw / width
}
-- | Viewer parameters for a typical smartphone screen.
phoneViewer :: ViewerParameters
phoneViewer = viewerGeometry 5.27 2.80 12
-- | Viewer parameters for a typical laptop screen.
laptopViewer :: ViewerParameters
laptopViewer = viewerGeometry 13.625 7.875 24
-- | Viewer parameters for a typical desktop monitor.
desktopViewer :: ViewerParameters
desktopViewer = viewerGeometry 20.75 11.625 32
-- | Viewer parameters for a typical projector.
projectorViewer :: ViewerParameters
projectorViewer =
def
{
displayAspectRatio = 1.6 / 1.0
, displayThrowRatio = 1.5 / 1.0
}
-- | Compute the field of view for viewer parameters.
fieldOfView :: ViewerParameters -- ^ The viewer parameters
-> GLdouble -- ^ The field of view, in degrees.
fieldOfView ViewerParameters{..} = 2 * atan2 0.5 displayThrowRatio * degree
-- | Construct a reshape callback from viewer parameters. This simply sets the frustum based on the viewer parameters and the size of the viewport.
reshape :: ViewerParameters -- ^ The viewer parameters.
-> ReshapeCallback -- ^ The reshape callback.
reshape vp@ViewerParameters{..} wh@(Size w h) =
do
viewport $=! (Position 0 0, wh)
matrixMode $=! Projection
loadIdentity
perspective (fieldOfView vp) (fromIntegral w / fromIntegral h) distanceNearPlane distanceFarPlane
matrixMode $=! Modelview 0
-- | Create an action look at the scene according to the viewer parameters.
loadViewer :: ViewerParameters -- ^ The viewer parameters.
-> DlpEye -- ^ The eye from which to view.
-> IO () -- ^ An action for looking at the scene using the specified eye and viewer parameters.
loadViewer ViewerParameters{..} eye =
do
loadIdentity
let
offset =
case eye of
LeftDlp -> -1/2
RightDlp -> 1/2
Vertex3 xEye yEye zEye = eyePosition
Vector3 dxEye dyEye dzEye = eyeSeparation
lookAt
(Vertex3 (xEye + offset * dxEye) (yEye + offset * dyEye) (zEye + offset * dzEye))
sceneCenter
eyeUpward
-- | Construct a DLP display from a display callback.
dlpViewerDisplay :: DlpEncoding -- ^ The DLP encoding.
-> ViewerParameters -- ^ The viewer parameters.
-> DisplayCallback -- ^ The display callback.
-> DlpDisplay -- ^ The DLP display data for using the specified encoding, viewer parameters, and display callback.
dlpViewerDisplay encoding viewerParameters display =
def
{
dlpEncoding = encoding
, doDisplay = \eye -> loadViewer viewerParameters eye >> display
}