packages feed

accelerate-examples-1.3.0.0: examples/ray/Scene/Object.hs

{-# LANGUAGE DeriveAnyClass  #-}
{-# LANGUAGE DeriveGeneric   #-}
{-# LANGUAGE PatternSynonyms #-}

module Scene.Object
  where

-- friends
import Common.Type

-- frenemies
import Data.Array.Accelerate                                    as A
import Data.Array.Accelerate.Control.Lens
import Data.Array.Accelerate.Data.Bits
import Data.Array.Accelerate.Data.Colour.RGB
import Data.Array.Accelerate.Linear.Metric
import Data.Array.Accelerate.Linear.V3
import Data.Array.Accelerate.Linear.Vector

-- standard library
import qualified Prelude                                        as P


-- | All objects in the scene
--
type Objects = (Vector Sphere, Vector Plane)

-- | Objects in the world. Accelerate does not have sum types, so define each
--   object separately (and hope this works out...)
--
data Sphere = Sphere_ Position Float Colour Float
  deriving (Show, Generic, Elt)

data Plane = Plane_ Position Direction Colour Float
  deriving (Show, Generic, Elt)

pattern Sphere :: Exp Position -> Exp Float -> Exp Colour -> Exp Float -> Exp Sphere
pattern Sphere { spherePos, sphereRadius, sphereColor, sphereShine } =
  Pattern ( spherePos, sphereRadius, sphereColor, sphereShine )
{-# COMPLETE Sphere #-}

pattern Plane :: Exp Position -> Exp Direction -> Exp Colour -> Exp Float -> Exp Plane
pattern Plane { planePos, planeNormal, planeColor, planeShine } =
  Pattern ( planePos, planeNormal, planeColor, planeShine )
{-# COMPLETE Plane #-}

type PlaneCheck = Plane

planeCheckPos    :: Exp PlaneCheck -> Exp Position
planeCheckNormal :: Exp PlaneCheck -> Exp Direction
planeCheckShine  :: Exp PlaneCheck -> Exp Float

planeCheckPos    = planePos
planeCheckNormal = planeNormal
planeCheckShine  = planeShine


-- | Compute the distance to the surface of a sphere
--
distanceToSphere
    :: Exp Sphere               -- ^ Object to intersect
    -> Exp Position             -- ^ Ray cast from this point...
    -> Exp Direction            -- ^ ...along this direction
    -> Exp (Bool, Float)        -- ^ Distance to intersection, if there is one
distanceToSphere sphere origin direction
  = let
        pos     = spherePos sphere
        radius  = sphereRadius sphere

        p       = origin + ((pos - origin) `dot` direction) *^ direction
        d_cp    = norm (p - pos)
        sep     = p - origin
        miss    = d_cp >= radius || sep `dot` direction <= 0
    in
    miss ? ( lift (False, infinity)
           , lift (True,  norm sep - sqrt (radius * radius - d_cp * d_cp)) )


-- | Compute the distance to the surface of a Plane
--
distanceToPlane
    :: Exp Plane                -- ^ Plane to intersect
    -> Exp Position             -- ^ Ray cast from this point
    -> Exp Direction            -- ^ ...along this direction
    -> Exp (Bool, Float)        -- ^ Distance to intersection, if there is one
distanceToPlane plane origin direction
  = let
        pos             = planePos plane
        normal          = planeNormal plane
        theta           = direction `dot` normal        -- TLM: name?
    in
    theta >= 0 ? ( lift (False, infinity)
                 , lift (True,  ((pos - origin) `dot` normal) / theta) )


-- | The maximum representable floating point value
--
infinity :: Exp Float
infinity = constant (P.encodeFloat m n)
  where
    a           = undefined :: Float
    b           = P.floatRadix a
    e           = P.floatDigits a
    (_, e')     = P.floatRange a
    m           = b P.^ e - 1
    n           = e' - e


-- | Compute the surface normal of a sphere at a point
--
sphereNormal
    :: Exp Sphere
    -> Exp Position             -- ^ A point on the surface of the sphere
    -> Exp Direction            -- ^ Normal at that point
sphereNormal sphere point
  = normalize (point - spherePos sphere)


-- | A checkerboard pattern along the x/z axis
--
checkers :: Exp Position -> Exp Colour
checkers pos
  = let
        x       = pos ^. _x
        z       = pos ^. _z
        v1      = (A.truncate (x / 100) :: Exp Int32) `mod` 2
        v2      = (A.truncate (z / 100) :: Exp Int32) `mod` 2
        v3      = A.fromIntegral . boolToInt $ x A.< 0.0
        v4      = A.fromIntegral . boolToInt $ z A.< 0.0
    in
    v1 `xor` v2 `xor` v3 `xor` v4 == 1 {- True -}
      ? ( rgb 1.0 1.0 1.0
        , rgb 0.4 0.4 0.4 )