module RSAGL.Scene.LightSource
(LightSource(..),
skylight,
MapLightSource,
mapLight,
mapAmbient,
mapBoth,
mapLightSource,
isNoLight,
infiniteLightSourceOf,
setLightSourcesToOpenGL,
setLightSourceToOpenGL)
where
import RSAGL.Math.Vector
import RSAGL.Modeling.Color as Color
import RSAGL.Math.Affine
import RSAGL.Scene.CoordinateSystems
import Graphics.UI.GLUT as GLUT
import RSAGL.Types
import Data.List as List
import Data.Monoid
data LightSource =
DirectionalLight { lightsource_direction :: Vector3D,
lightsource_color :: Color.RGB,
lightsource_ambient :: Color.RGB }
| PointLight { lightsource_position :: Point3D,
lightsource_radius :: Distance,
lightsource_color :: Color.RGB,
lightsource_ambient :: Color.RGB }
| NoLight
skylight :: Vector3D -> Color.RGB -> LightSource
skylight v c = DirectionalLight {
lightsource_direction = v,
lightsource_color = scaleRGB 0.7208681020859709 c,
lightsource_ambient = scaleRGB 0.27913189791402915 c }
isNoLight :: LightSource -> Bool
isNoLight NoLight = True
isNoLight _ = False
data MapLightSource = MapLightSource { map_light, map_ambient :: (RGB -> RGB), map_affine :: AffineTransformation }
instance Monoid MapLightSource where
mempty = MapLightSource { map_light = id, map_ambient = id, map_affine = id }
mappend x y = MapLightSource {
map_light = map_light x . map_light y,
map_ambient = map_ambient x . map_ambient y,
map_affine = map_affine x . map_affine y }
instance AffineTransformable MapLightSource where
transform m f = f { map_affine = transform m . map_affine f }
mapLight :: (RGB -> RGB) -> MapLightSource
mapLight f = mempty { map_light = f }
mapAmbient :: (RGB -> RGB) -> MapLightSource
mapAmbient f = mempty { map_ambient = f }
mapBoth :: (RGB -> RGB) -> MapLightSource
mapBoth f = mapLight f `mappend` mapAmbient f
mapLightSource :: MapLightSource -> LightSource -> LightSource
mapLightSource f (DirectionalLight source_direction source_color source_ambient) =
transformation (map_affine f) $ DirectionalLight source_direction (map_light f source_color) (map_ambient f source_ambient)
mapLightSource f (PointLight source_position source_radius source_color source_ambient) =
transformation (map_affine f) $ PointLight source_position source_radius (map_light f source_color) (map_ambient f source_ambient)
mapLightSource _ NoLight = NoLight
infiniteLightSourceOf :: LightSource -> LightSource
infiniteLightSourceOf NoLight = NoLight
infiniteLightSourceOf (d@(DirectionalLight {})) = d
infiniteLightSourceOf (p@PointLight {}) = DirectionalLight {
lightsource_direction = vectorToFrom (lightsource_position p) origin_point_3d,
lightsource_color = scaleRGB scale_factor $ lightsource_color p,
lightsource_ambient = scaleRGB scale_factor $ lightsource_ambient p }
where scale_factor = f2f $ (distanceSquared $ lightsource_radius p) / (distanceBetweenSquared origin_point_3d (lightsource_position p))
instance AffineTransformable LightSource where
transform _ NoLight = NoLight
transform m (dl@(DirectionalLight {})) = dl { lightsource_direction = transform m $ lightsource_direction dl }
transform m (pl@(PointLight {})) = pl {
lightsource_position = transform m $ lightsource_position pl,
lightsource_radius = transform m $ lightsource_radius pl }
setLightSourcesToOpenGL :: [LightSource] -> IO ()
setLightSourcesToOpenGL lss =
do max_lights <- GLUT.get maxLights
mapM_ setLightSourceToOpenGL $ genericTake max_lights $ zip (map Light [0..]) (lss ++ repeat NoLight)
setLightSourceToOpenGL :: (Light,LightSource) -> IO ()
setLightSourceToOpenGL (l,NoLight) = light l $= Disabled
setLightSourceToOpenGL (l,dl@DirectionalLight { lightsource_color = Color.RGB cr cg cb,
lightsource_ambient = Color.RGB ar ag ab }) =
do let Vector3D vx vy vz = vectorNormalize $ lightsource_direction dl
light l $= Enabled
ambient l $= Color4 (f2f ar) (f2f ag) (f2f ab) 1.0
GLUT.specular l $= Color4 (f2f cr) (f2f cg) (f2f cb) 1.0
diffuse l $= Color4 (f2f cr) (f2f cg) (f2f cb) 1.0
position l $= Vertex4 (f2f vx) (f2f vy) (f2f vz) 0
attenuation l $= (1,0,0)
setLightSourceToOpenGL (l,pl@(PointLight { lightsource_position = (Point3D px py pz),
lightsource_color = Color.RGB cr cg cb,
lightsource_ambient = Color.RGB ar ag ab })) =
do light l $= Enabled
ambient l $= Color4 (f2f ar) (f2f ag) (f2f ab) 1.0
GLUT.specular l $= Color4 (f2f cr) (f2f cg) (f2f cb) 1.0
diffuse l $= Color4 (f2f cr) (f2f cg) (f2f cb) 1.0
position l $= Vertex4 (f2f px) (f2f py) (f2f pz) 1
attenuation l $= (0.01,0,f2f $ recip $ distanceSquared $ lightsource_radius pl)