{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
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.Linear.Metric
import Data.Array.Accelerate.Linear.V3
import Data.Array.Accelerate.Linear.Vector
import Data.Array.Accelerate.Array.Sugar ( Elt(..), EltRepr, Tuple(..), fromTuple, toTuple )
import Data.Array.Accelerate.Product
import Data.Array.Accelerate.Smart
import Data.Array.Accelerate.Data.Colour.RGB
-- standard library
import Data.Typeable
import qualified Prelude as P
-- | All objects in the scene
--
type Objects = (Array DIM1 Sphere, Array DIM1 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 (P.Eq, P.Show, Typeable)
spherePos :: Exp Sphere -> Exp Position
sphereColor :: Exp Sphere -> Exp Colour
sphereShine :: Exp Sphere -> Exp Float
sphereRadius :: Exp Sphere -> Exp Float
data Plane = Plane Position Direction Colour Float
deriving (P.Eq, P.Show, Typeable)
planePos :: Exp Plane -> Exp Position
planeNormal :: Exp Plane -> Exp Direction
planeColor :: Exp Plane -> Exp Colour
planeShine :: Exp Plane -> Exp Float
type PlaneCheck = Plane
planeCheckPos :: Exp PlaneCheck -> Exp Position
planeCheckNormal :: Exp PlaneCheck -> Exp Direction
planeCheckShine :: Exp PlaneCheck -> Exp Float
-- | 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 )
-- Get Objects into Accelerate -------------------------------------------------
-- Because the Object types aren't parameterised, the type of the individual
-- fields are fixed. Thus we can't unlift from (Exp Sphere) to a Sphere of Exp
-- things. Poop.
--
spherePos s = Exp $ SuccTupIdx (SuccTupIdx (SuccTupIdx ZeroTupIdx)) `Prj` s
sphereRadius s = Exp $ SuccTupIdx (SuccTupIdx ZeroTupIdx) `Prj` s
sphereColor s = Exp $ SuccTupIdx ZeroTupIdx `Prj` s
sphereShine s = Exp $ ZeroTupIdx `Prj` s
planePos p = Exp $ SuccTupIdx (SuccTupIdx (SuccTupIdx ZeroTupIdx)) `Prj` p
planeNormal p = Exp $ SuccTupIdx (SuccTupIdx ZeroTupIdx) `Prj` p
planeColor p = Exp $ SuccTupIdx ZeroTupIdx `Prj` p
planeShine p = Exp $ ZeroTupIdx `Prj` p
planeCheckPos = planePos
planeCheckNormal = planeNormal
planeCheckShine = planeShine
-- Sphere
-- ------
type instance EltRepr Sphere = EltRepr (Position, Float, Colour, Float)
instance Elt Sphere where
eltType (_ :: Sphere) = eltType (undefined :: (Position, Float, Colour, Float))
toElt sphere = let (p,r,c,s) = toElt sphere in Sphere p r c s
fromElt (Sphere p r c s) = fromElt (p, r, c, s)
instance IsProduct Elt Sphere where
type ProdRepr Sphere = ProdRepr (Position, Float, Colour, Float)
fromProd _ (Sphere p r c s) = fromTuple (p, r, c, s)
toProd _ t = let (p, r, c, s) = toTuple t in Sphere p r c s
prod cst _ = prod cst (undefined :: (Position, Float, Colour, Float))
instance Lift Exp Sphere where
type Plain Sphere = Sphere
lift (Sphere p r c s)
= Exp . Tuple
$ NilTup `SnocTup` lift p `SnocTup` lift r `SnocTup` lift c `SnocTup` lift s
-- Plane
-- -----
type instance EltRepr Plane = EltRepr (Position, Direction, Colour, Float)
instance Elt Plane where
eltType (_ :: Plane) = eltType (undefined :: (Position, Direction, Colour, Float))
toElt plane = let (p,n,c,s) = toElt plane in Plane p n c s
fromElt (Plane p n c s) = fromElt (p, n, c, s)
instance IsProduct Elt Plane where
type ProdRepr Plane = ProdRepr (Position, Direction, Colour, Float)
fromProd _ (Plane p n c s) = fromTuple (p, n, c, s)
toProd _ t = let (p, n, c, s) = toTuple t in Plane p n c s
prod cst _ = prod cst (undefined :: (Position, Direction, Colour, Float))
instance Lift Exp Plane where
type Plain Plane = Plane
lift (Plane p n c s)
= Exp . Tuple
$ NilTup `SnocTup` lift p `SnocTup` lift n `SnocTup` lift c `SnocTup` lift s
{--
-- Checkered Plane
-- ---------------
type instance EltRepr PlaneCheck = EltRepr (Position, Direction, Float)
instance Elt PlaneCheck where
eltType (_ :: PlaneCheck) = eltType (undefined :: (Position, Direction, Float))
toElt plane = let (p,n,s) = toElt plane in PlaneCheck p n s
fromElt (PlaneCheck p n s) = fromElt (p, n, s)
instance IsTuple PlaneCheck where
type TupleRepr PlaneCheck = TupleRepr (Position, Direction, Float)
fromTuple (PlaneCheck p n s) = fromTuple (p, n, s)
toTuple t = let (p, n, s) = toTuple t in PlaneCheck p n s
instance Lift Exp PlaneCheck where
type Plain PlaneCheck = PlaneCheck
lift (PlaneCheck p n s)
= Exp . Tuple
$ NilTup `SnocTup` lift p `SnocTup` lift n `SnocTup` lift s
--}