module Hittable (Shape (..), Circle (..), Hit (..), isFrontFace) where import Linear.V3 import Linear.Vector import Material import Maths import Ray class Shape s where testHit :: Ray -> s -> Maybe Hit data Circle = Circle {center :: V3 Double, radius :: Double, material :: Material} data Hit = Hit {root :: Double, p :: V3 Double, n :: V3 Double, m :: Material} getFaceNormal :: Ray -> V3 Double -> V3 Double getFaceNormal (Ray o d) n | (d `dotP` n) < 0.0 = n | otherwise = (-1.0) *^ n isFrontFace :: Ray -> V3 Double -> Bool isFrontFace (Ray o d) n | (d `dotP` n) < 0.0 = True | otherwise = False -- t_min 0 t_max infinity... need closest so far instance Shape Circle where testHit (Ray o d) (Circle center r m) = if discriminant < 0.0 then Nothing else if root > 0.001 then Just (Hit root p (getFaceNormal (Ray o d) ((p - center) ^/ r)) m) else if rootP > 0.001 then Just (Hit rootP pp (getFaceNormal (Ray o d) ((pp - center) ^/ r)) m) else Nothing where co = o - center a = lengthSquared d -- a vector dotted with itself is = lengthSquared b = co `dotP` d c = lengthSquared co - r ^ 2 discriminant = b ^ 2 - a * c root = (b * (-1.0) - sqrt discriminant) / a rootP = (b * (-1.0) + sqrt discriminant) / a p = rayAt (Ray o d) root pp = rayAt (Ray o d) rootP -- would make sense to build a Functor for shapes