packages feed

GlomeView-0.2: Glome.hs

import Control.DeepSeq(NFData, rnf)
import Control.Monad.Par
import Graphics.UI.SDL as SDL
import Control.Monad(forM_)
import Foreign.Storable
import Foreign.Ptr
import Data.Word
import Data.Time.Clock.POSIX

import Data.Vector.Generic.Base
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Generic as GV

import Data.Glome.Scene as Scene
import Data.Glome.Trace as Trace
import Data.Glome.Spd as Spd
import TestScene


maxdepth = 3 -- recursion depth for reflection/refraction

-- compute ray, invoke trace function, return color
get_color :: Flt -> Flt -> Scene -> (Scene.Color,Flt)
get_color x y scn = 
 let (Scene sld lights (Camera pos fwd up right) dtex bgcolor) = scn
     dir = vnorm $ vadd3 fwd (vscale right (-x)) (vscale up y)
     ray = (Ray pos dir) 
 in
  ((Trace.trace scn ray infinity maxdepth),0)

get_color' :: Flt -> Flt -> Scene -> (Scene.Color,Flt)
get_color' x y _ =
  (Scene.Color x y (x+y), 0)

-- compute a 2x2 packet of four rays from corners of box
get_packet :: Flt -> Flt -> Flt -> Flt -> Scene -> PacketColor
get_packet x1 y1 x2 y2 scn =
 let (Scene sld lights (Camera pos fwd up right) dtex bgcolor) = scn
     dir1 = vnorm $ vadd3 fwd (vscale right (-x1)) (vscale up y1)
     dir2 = vnorm $ vadd3 fwd (vscale right (-x2)) (vscale up y1)
     dir3 = vnorm $ vadd3 fwd (vscale right (-x1)) (vscale up y2)
     dir4 = vnorm $ vadd3 fwd (vscale right (-x2)) (vscale up y2)
     ray1 = Ray pos dir1
     ray2 = Ray pos dir2
     ray3 = Ray pos dir3
     ray4 = Ray pos dir4
 in trace_packet scn ray1 ray2 ray3 ray4 infinity maxdepth

cap1 :: Flt -> Flt
cap1 x
 | x >= 1 = 1-delta
 | otherwise = x      

-- mapRGB does this, but we need a pixel format...
rgb :: Int -> Int -> Int -> Pixel
rgb r g b = Pixel $ (fromIntegral r)*(256*256) + (fromIntegral g)*256 + (fromIntegral b)

rgbf :: Flt -> Flt -> Flt -> Pixel
rgbf r g b = Pixel $ (floor ((cap1 r)*256))*(256*256) +
                     (floor ((cap1 g)*256))*256 +
                     (floor ((cap1 b)*256))

xres = 720
yres = 480
blocksize = 64


-- from http://stackoverflow.com/questions/5443259/loading-a-opengl-image-with-sdl-image
setPixel32 :: Surface -> Int -> Int -> Pixel -> IO ()
setPixel32 surf x y (Pixel pixel) = do
  ps <- surfaceGetPixels surf
  if x >= 0 && x < surfaceGetWidth surf && y >= 0 && y < surfaceGetHeight surf
    then pokeElemOff (castPtr ps :: Ptr Word32) offset pixel
    else error "setPixel32 -- bad coordinate"
 where offset = y * (fromIntegral $ surfaceGetPitch surf `div` 4) + x

{-
renderTile :: Rect -> Surface -> IO ()
renderTile (Rect xmin xmax width height) (surf = do
  -}

-- r g b debug index
data Tile = Tile Rect (UV.Vector (Flt, Flt, Flt, Flt))

instance NFData Tile where
  rnf (Tile r v) = rnf v


-- We pass in the Rect for the whole image, and a sub-Rect for the tile
-- we want to render.
renderTile :: Rect -> Rect -> Scene -> Tile
renderTile (Rect _ _ width height) t@(Rect xtmin ytmin twidth theight) scene =
  let widthf = fromIntegral width
      heightf = fromIntegral height
      gen i =
        let xf = fromIntegral $ xtmin + (mod i twidth)
            yf = fromIntegral $ ytmin + (div i twidth)
            xcoord = (((xf/widthf)*2)-1) * (widthf/heightf)
            ycoord = -(((yf/heightf)*2)-1)
            (Scene.Color r g b, d) = get_color xcoord ycoord scene
            --(r,g,b,d) = (0.5, 0, 0, 0)
        in
          (r, g, b, d)
          
  in Tile t (UV.generate (twidth * theight) gen)

blitTile :: Surface -> Tile -> IO ()
blitTile surf (Tile (Rect xtmin ytmin twidth theight) pixels) =
  UV.zipWithM_ drawf (UV.fromList [0..((twidth*theight)-1)]) pixels
  where
    drawf i (r, g, b, d) = 
      setPixel32 surf (xtmin + (mod i twidth)) (ytmin + (div i twidth)) (rgbf r g b)


     {- do
      _ <- fillRect
             surf
             (Just (Rect (xtmin + (mod i twidth)) 
                         (ytmin + (div i twidth))  1 1))
             (rgbf r g b)
      return () -}

-- Given an Int, return a list of Ints broken down into blocksize chunks.
-- Each chunk is an (offset,size) tuple.
chunk size blocksize =
  go 0
    where
      go pos =
        if pos + blocksize >= size
        then [(pos, (size-pos))]
        else (pos, blocksize) : (go (pos+blocksize))

renderTiles :: Surface -> Scene -> Int -> IO ()
renderTiles surf scene blocksize = do
  srect@(Rect _ _ width height) <- getClipRect surf
  let xchunks = chunk width blocksize
  let ychunks = chunk height blocksize
  let blocks = [Rect xpos ypos width height   | (xpos, width) <- xchunks, (ypos, height) <- ychunks]
  let tiles = runPar $ parMap (\block -> renderTile srect block scene) blocks
  forM_ tiles (blitTile surf)
  

-- Simple version, not amenable to parallelization.
render :: Surface -> Scene -> IO ()
render surf scene = do
  Rect xmin ymin width height <- getClipRect surf
  let xmax = xmin+width
  let ymax = ymin+height
  forM_ [xmin..xmax] (\x ->
    forM_ [ymin..ymax] (\y ->
      let xf = fromIntegral x
          yf = fromIntegral y
          widthf = fromIntegral width
          heightf = fromIntegral height
          xcoord = (((xf/widthf)*2)-1) * (widthf/heightf)
          ycoord = -(((yf/heightf)*2)-1)
          (Scene.Color r g b, d) = get_color xcoord ycoord scene
      in
        do _ <- fillRect surf (Just (Rect x y 1 1)) (rgbf r g b)
           return ()
                       )
                     )

quitHandler :: IO ()
quitHandler = do
  e <- waitEvent
  case e of
    Quit -> return ()
    otherwise -> quitHandler

main = do
  SDL.init [InitVideo, InitTimer, InitJoystick]
  setVideoMode xres yres 32 []
  screen <- getVideoSurface

  -- HWSurface seems to be slightly faster.
  img <- createRGBSurface [HWSurface] xres yres 32 0 0 0 0

  setupt1 <- getPOSIXTime
  scene <- TestScene.scn

  print $ "(primitives,transforms,bounding objects): " ++ (show (primcount_scene scene))
  setupt2 <- getPOSIXTime
  print $ "scene setup: " ++ (show (setupt2-setupt1))

  forM_ [1..1] (\_ ->
    do rendert1 <- getPOSIXTime
       renderTiles img scene blocksize
       rendert2 <- getPOSIXTime
       print $ "render: " ++ (show (rendert2-rendert1)))

  blitt1 <- getPOSIXTime
  blitSurface img Nothing screen Nothing
  SDL.flip screen
  blitt2 <- getPOSIXTime
  print $ "blit: " ++ (show (blitt2-blitt1))

  quitHandler