packages feed

lambdacube-engine-0.1.1: Graphics/LambdaCube/Resource.hs

module Graphics.LambdaCube.Resource where

import qualified Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Control.Monad
import System.Directory
import System.FilePath

import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString as SB
import Data.ByteString.Internal as B
import Codec.Archive.Zip
import System.Log.Logger

import Graphics.LambdaCube.Loader.CompositorScript
import Graphics.LambdaCube.Loader.MaterialScript
import Graphics.LambdaCube.Loader.ResourceScript
import Graphics.LambdaCube.Compositor
import Graphics.LambdaCube.Material
import Graphics.LambdaCube.Texture
import Graphics.LambdaCube.GpuProgram
import Graphics.LambdaCube.World

-- load resources into resource library (initResources :: [ResourceGroup] -> ResourceLibrary)

{-
    Stores data about:
      - loaded meshes (name)
      - loaded and compiled materials (name)
-}

-- TODO: load all available materials
-- scan path tree searching for *.material files
data (Texture t, GpuProgram p, LinkedGpuProgram lp) => ResourceItem t p lp
    = RI_Material   (String,Material t lp)
    | RI_GpuProgram (String,GpuProgramDescriptor p)
    | RI_Compositor (String,Compositor t lp)
                
addConfig cfgFile world = do
    Just rp <- loadResourcesCfg cfgFile
    addResourceLibrary rp world

addResourceLibrary resPath w@World { wrResource = res } = do
    -- fold on every group and path
    -- getDirectoryContents :: FilePath -> IO [FilePath]
    -- doesDirectoryExist :: FilePath -> IO Bool
    -- takeExtension :: FilePath -> String
    -- TODO:
    --  scan whole resource path searching for resource files and load them:
    --    .material
    --    .program
    --  add normal (uncompiled) materials to material library
    let loadResGrp s (_,l) = foldM loadRes s l
        loadRes l (PathZip,n) = return l
        loadRes l p@(PathDir,n) = do
            ok <- doesDirectoryExist n
            case ok of
                { True  -> do
                    c <- getDirectoryContents n
                    foldM (loadItem n) l c
                ; False -> return l
                }
        loadItem dn l n = do
            --parseMaterial -> Maybe (mats,verts,frags)
            case takeExtension n of
                { ".material" -> do
                    debugM "ResourceLibrary" $ "load: " ++ n
                    f <- B.readFile $ dn ++ "/" ++ n -- FIXME: this is temporary solution
                    mb <- parseMaterial n $ map B.w2c $ B.unpack f
                    case mb of
                        { Nothing   -> do
                            errorM  "ResourceLibrary" $ "Syntax error in file " ++ n
                            return l
                        ; Just (ml,vpl,fpl) -> do
                            let ms = [RI_Material (mtName m,m) | m <- ml]
                                vs = [RI_GpuProgram (gpdName m,m) | m <- vpl]
                                fs = [RI_GpuProgram (gpdName m,m) | m <- fpl]
                            return $ l ++ ms ++ vs ++ fs
                        }
                ; ".compositor" -> do
                    debugM "ResourceLibrary" $ "load: " ++ n
                    f <- B.readFile $ dn ++ "/" ++ n -- FIXME: this is temporary solution
                    mb <- parseCompositor n $ map B.w2c $ B.unpack f
                    case mb of
                        { Nothing   -> do
                            errorM  "ResourceLibrary" $ "Syntax error in file " ++ n
                            return l
                        ; Just cl -> do
                            return $ l ++ [RI_Compositor (cmpName m,m) | m <- cl]
                        }
                ; _ -> return l
                }
        insertList = List.foldl' (flip (uncurry Map.insert))
    l <- foldM loadResGrp [] resPath
    return w { wrResource = res 
                { rlResourceGroups = resPath ++ rlResourceGroups res
                , rlMaterialMap     = insertList (rlMaterialMap res)    [m | (RI_Material m) <- l]
                , rlGpuProgramMap   = insertList (rlGpuProgramMap res)  [m | (RI_GpuProgram m) <- l]
                , rlCompositorMap   = insertList (rlCompositorMap res)  [m | (RI_Compositor m) <- l]
                }
             }
               --ResourceLibrary (resPath++path) meshes (Map.union mats materialMap) texes (Map.union shads shaderMap) progs

{-
loadResourceGroup
dropResourceGroup
-}

{-
--compileMaterial
-- load textures from disk to opengl
-- drops unnecessary techniques (include,exlude rules)
compileMaterial :: ResourceLibrary -> String -> IO (ResourceLibrary,Material)
compileMaterial rlib m = do
    let mm = (rlMaterialMap rlib)
    if Map.notMember m mm then error ("Unknown material: " ++ m) else return ()
    let (mt,mcmt) = mm Map.! m
    (rlib',cmt) <- if isJust mcmt then return (rlib,fromMaybe (error "fromJust 0") mcmt) else compMat rlib mt -- copy original material data and load texture units
    return (rlib',cmt) where
        compMat rl mat@(Material {mtTechnique=techs}) = do
            debugM "ResourceLibrary" $ "compiling material: " ++ m
            (rl',techs') <- foldM compTechnique (rl,[]) techs
            let matmap' = insert m (mat,Just cmt') (rlMaterialMap rl) 
                cmt' = mat{mtTechnique=techs'}
            return (rl'{rlMaterialMap=matmap'},cmt')
        compTechnique (rl,l) t@(Technique {tcPass=p}) = do
            excl <- excludeTechnique t
            if excl then return (rl, l) else do
                (rl',p') <- foldM compPass (rl,[]) p
                return (rl',(l ++ [t{tcPass=p'}]))
        compPass (rl,l) p = do
            (rl',tu') <- foldM compTexUnit (rl,[]) $ psTextureUnit p
            let (ProgRefName prog) = psShaderProgram p
            (rl'',prog') <- compileProgram rl' prog
            return (rl'',(l ++ [p{psTextureUnit=tu', psShaderProgram=(ProgRefObj prog')}]))
        compTexUnit (rl,l) tu = do
            (rl',texid) <- loadTexture rl $ head $ tuTexture tu
            return (rl',(l ++ [tu{tuTextureObject=Just [texid]}]))
        excludeTechnique t@(Technique {tcPass=p}) = do
            -- exclude if shaders are not supported
            version <- GL.get (GL.majorMinor GL.glVersion)
            let reqShaders = not $ null $ filter (\(Pass {psShaderProgram=(ProgRefName (vp,pp))})-> isJust vp || isJust pp) p
            if reqShaders && version < (2,0) then return True else return False
-}