content: {-# LANGUAGE BangPatterns #-}
  
  module Trace where
  import Object
  import Light
  import Vec3
  
  
  -- Cast a single ray into the scene
  traceRay
          :: [Object]     -- objects in scene
          -> [Light]      -- direct lights in scene
          -> Color        -- ambient light in scene
          -> Vec3         -- origin of ray
          -> Vec3         -- direction of ray
          -> Int          -- maximum reflection count
          -> Color        -- visible color for this ray
  
  traceRay !objs !lights !ambient !(Vec3 gX gY gZ) !dir !limit
   = go gX gY gZ dir limit
   where
         -- too many reflections,
         -- give up incase we've found two parallel mirrors..
         go _ _ _ _ 0
          = Vec3 0.0 0.0 0.0
  
         go !oX !oY oZ !dir' !bounces
          = castRay_continuation objs (Vec3 oX oY oZ) dir'
              -- ray didn't intersect any objects
              (Vec3 0.0 0.0 0.0)
  
              -- ray hit an object
              (\obj point@(Vec3 pX' pY' pZ')
               -> let
                  -- get the surface normal at that point.
                  !normal    = surfaceNormal obj point
  
                  -- result angle of ray after reflection.
                  !newdir    = dir - normal `mulsV3` (2.0 * (normal `dotV3` dir))
  
                  -- determine the direct lighting at this point
                  !direct    = applyLights objs point normal lights
  
                  -- see if ray hits anything else.
                  !refl      = go pX' pY' pZ' newdir (bounces - 1)
  
                  -- total lighting is the direct lights plus ambient
                  !lighting  = direct + ambient
  
                  -- total incoming light is direct lighting plus reflections
                  !color     = colorOfObject obj point
                  !shine     = shineOfObject obj point
  
                  !light_in  = refl    `mulsV3` shine
                             + lighting `mulsV3` (1.0 - shine)
  
                  -- Outgoing light is incoming light modified by surface color.
                  -- We also need to clip it incase the sum of all incoming lights
                  --  will be too bright to display.
                  !light_out = clipV3 (light_in * color) 1.0
  
                in light_out)
  {-# INLINE traceRay #-}
  