diff --git a/Pioneers.cabal b/Pioneers.cabal index 27a407f..48b2fc3 100644 --- a/Pioneers.cabal +++ b/Pioneers.cabal @@ -16,6 +16,9 @@ executable Pioneers Map.Graphics, Map.Creation, Map.StaticMaps, + IQM.Types, + IQM.TestMain, + IQM.Parser, Render.Misc, Render.Render, Render.RenderObject, @@ -41,6 +44,7 @@ executable Pioneers lens >=4.0, SDL2 >= 0.1.0, time >=1.4.0, - GLUtil >= 0.7 + GLUtil >= 0.7, + attoparsec >= 0.11.2 Default-Language: Haskell2010 diff --git a/sample.iqm b/sample.iqm new file mode 100644 index 0000000..44a9d46 Binary files /dev/null and b/sample.iqm differ diff --git a/src/Importer/IQM/Parser.hs b/src/Importer/IQM/Parser.hs new file mode 100644 index 0000000..09efd9a --- /dev/null +++ b/src/Importer/IQM/Parser.hs @@ -0,0 +1,132 @@ +module Importer.IQM.Parser where + +import Importer.IQM.Types +import Data.Attoparsec.ByteString.Char8 +import Data.Attoparsec.ByteString +import Data.Attoparsec +import Data.ByteString.Char8 (pack) +import Data.ByteString (split, null) +import Data.Word +import Data.Int +import Data.List (foldl1) +import Foreign.C.Types +import Unsafe.Coerce + +import Prelude as P hiding (take, null) + +w8Toint :: Integral a => a -> a -> a +w8Toint i add = 256*i + add + + +int16 :: Parser Int16 +int16 = do + a <- anyWord8 :: Parser Word8 + b <- anyWord8 :: Parser Word8 + return $ foldl1 w8Toint $ map fromIntegral [b,a] + +int32 :: Parser Int32 +int32 = do + a <- anyWord8 :: Parser Word8 + b <- anyWord8 :: Parser Word8 + c <- anyWord8 :: Parser Word8 + d <- anyWord8 :: Parser Word8 + return $ foldl1 w8Toint $ map fromIntegral [d,c,b,a] + +readHeader = do + string (pack "INTERQUAKEMODEL\0") + v <- int32 + -- when v /= 2 then --TODO: error something + size <- int32 + flags <- int32 + num_text <- int32 + ofs_text <- int32 + num_meshes <- int32 + ofs_meshes <- int32 + num_vertexarrays <- int32 + num_vertexes <- int32 + ofs_vertexarrays <- int32 + num_triangles <- int32 + ofs_triangles <- int32 + ofs_adjacency <- int32 + num_joints <- int32 + ofs_joints <- int32 + num_poses <- int32 + ofs_poses <- int32 + num_anims <- int32 + ofs_anims <- int32 + num_frames <- int32 + num_framechannels <- int32 + ofs_frames <- int32 + ofs_bounds <- int32 + num_comment <- int32 + ofs_comment <- int32 + num_extensions <- int32 + ofs_extensions <- int32 + return (IQMHeader { version = v + , filesize = size + , flags = flags + , num_text = num_text + , ofs_text = ofs_text + , num_meshes = num_meshes + , ofs_meshes = ofs_meshes + , num_vertexarrays = num_vertexarrays + , num_vertexes = num_vertexes + , ofs_vertexarrays = ofs_vertexarrays + , num_triangles = num_triangles + , ofs_triangles = ofs_triangles + , ofs_adjacency = ofs_adjacency + , num_joints = num_joints + , ofs_joints = ofs_joints + , num_poses = num_poses + , ofs_poses = ofs_poses + , num_anims = num_anims + , ofs_anims = ofs_anims + , num_frames = num_frames + , num_framechannels = num_framechannels + , ofs_frames = ofs_frames + , ofs_bounds = ofs_bounds + , num_comment = num_comment + , ofs_comment = ofs_comment + , num_extensions = num_extensions + , ofs_extensions = ofs_extensions + } + , 16+27*4) + +readMesh :: Parser IQMMesh +readMesh = do + name <- int32 + mat <- int32 + fv <- int32 + nv <- int32 + ft <- int32 + nt <- int32 + return IQMMesh + { meshName = if name == 0 then Nothing else Just (Mesh name) + , meshMaterial = mat + , meshFirstVertex = fv + , meshNumVertexes = nv + , meshFirstTriangle = ft + , meshNumTriangles = nt + } + +readMeshes :: Int -> Parser [IQMMesh] +readMeshes 1 = do + m <- readMesh + return [m] +readMeshes n = do + m <- readMesh + ms <- readMeshes (n-1) + return $ m:ms + +parseIQM :: Parser IQM +parseIQM = do + (h,soFar) <- readHeader + take $ (fromIntegral (ofs_text h)) - soFar + text <- take $ fromIntegral $ num_text h + meshes <- readMeshes (fromIntegral (num_meshes h)) + return IQM + { header = h + , texts = filter (not.null) (split (unsafeCoerce '\0') text) + , meshes = meshes + } + \ No newline at end of file diff --git a/src/Importer/IQM/Types.hs b/src/Importer/IQM/Types.hs new file mode 100644 index 0000000..8222e85 --- /dev/null +++ b/src/Importer/IQM/Types.hs @@ -0,0 +1,60 @@ +module Importer.IQM.Types where + +import Data.Int +import Data.ByteString + +newtype Mesh = Mesh Int32 deriving (Show, Eq) +newtype CParser a = Parser (a, Int64) + +-- Int32 or Int64 - depending on implementation. Format just specifies "uint". +-- 4-Byte indicates Int32 + +-- | ofs_* fields are relative tot he beginning of the iqmheader struct +-- ofs_* fields are set to 0 when data is empty +-- ofs_* fields are aligned at 4-byte-boundaries +data IQMHeader = IQMHeader + { version :: Int32 -- ^ Must be 2 + , filesize :: Int32 + , flags :: Int32 + , num_text :: Int32 + , ofs_text :: Int32 + , num_meshes :: Int32 + , ofs_meshes :: Int32 + , num_vertexarrays :: Int32 + , num_vertexes :: Int32 + , ofs_vertexarrays :: Int32 + , num_triangles :: Int32 + , ofs_triangles :: Int32 + , ofs_adjacency :: Int32 + , num_joints :: Int32 + , ofs_joints :: Int32 + , num_poses :: Int32 + , ofs_poses :: Int32 + , num_anims :: Int32 + , ofs_anims :: Int32 + , num_frames :: Int32 + , num_framechannels :: Int32 + , ofs_frames :: Int32 + , ofs_bounds :: Int32 + , num_comment :: Int32 + , ofs_comment :: Int32 + , num_extensions :: Int32 -- ^ stored as linked list, not as array. + , ofs_extensions :: Int32 + } deriving (Show, Eq) + + +data IQMMesh = IQMMesh + { meshName :: Maybe Mesh + , meshMaterial :: Int32 + , meshFirstVertex :: Int32 + , meshNumVertexes :: Int32 + , meshFirstTriangle :: Int32 + , meshNumTriangles :: Int32 + } deriving (Show, Eq) + +data IQM = IQM + { header :: IQMHeader + , texts :: [ByteString] + , meshes :: [IQMMesh] + } deriving (Show, Eq) + \ No newline at end of file diff --git a/src/Main.hs b/src/Main.hs index c9963cb..a8283e4 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -60,12 +60,21 @@ import Render.Render (initRendering, import UI.Callbacks import UI.GUIOverlay import Types +import Importer.IQM.Parser +import Data.Attoparsec.Char8 (parseTest) +import qualified Data.ByteString as B --import ThirdParty.Flippers import qualified Debug.Trace as D (trace) -------------------------------------------------------------------------------- + +testParser = do + B.readFile "sample.iqm" >>= parseTest parseIQM + +-------------------------------------------------------------------------------- + main :: IO () main = do SDL.withInit [InitVideo, InitAudio, InitEvents, InitTimer] $ do --also: InitNoParachute -> faster, without parachute!