3dmodels (empty) → 0.3.0
raw patch · 9 files changed
+1372/−0 lines, 9 filesdep +attoparsecdep +basedep +bytestringsetup-changed
Dependencies added: attoparsec, base, bytestring, linear, packer
Files
- 3dmodels.cabal +30/−0
- ChangeLog +8/−0
- Graphics/Model/DirectX.hs +186/−0
- Graphics/Model/MikuMikuDance.hs +19/−0
- Graphics/Model/MikuMikuDance/Loader.hs +530/−0
- Graphics/Model/MikuMikuDance/Types.hs +306/−0
- Graphics/Model/Obj.hs +126/−0
- LICENSE +165/−0
- Setup.hs +2/−0
+ 3dmodels.cabal view
@@ -0,0 +1,30 @@+-- Initial 3dmodels.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: 3dmodels+version: 0.3.0+synopsis: 3D model parsers+description: 3D model parsers+homepage: https://github.com/capsjac/3dmodels+license: LGPL-3+license-file: LICENSE+author: capsjac <capsjac at gmail dot com>+maintainer: capsjac <capsjac at gmail dot com>+copyright: (c) 2014 capsjac+category: Graphics+build-type: Simple+extra-source-files: ChangeLog+cabal-version: >=1.10+extra-source-files: ++source-repository head+ type: git+ location: https://github.com/capsjac/3dmodels++library+ exposed-modules: Graphics.Model.MikuMikuDance, Graphics.Model.DirectX, Graphics.Model.Obj, Graphics.Model.MikuMikuDance.Loader, Graphics.Model.MikuMikuDance.Types+ -- other-modules: + other-extensions: OverloadedStrings, RecordWildCards, TupleSections+ build-depends: base >=4.7 && <4.8, attoparsec >=0.12 && <0.13, bytestring >=0.10 && <0.11, linear >=1.10 && <1.11, packer >=0.1 && <0.2+ -- hs-source-dirs: + default-language: Haskell2010
+ ChangeLog view
@@ -0,0 +1,8 @@+Year 2014+ Aug 20, 15:40 Project start.+ Aug 21, 16:48 PMX 2.0 finished.+ 20:48 VMD finished.+ Aug 22, 21:16 PMD finished. -> 0.1.0.0+ Aug 23, 14:54 PMX 2.1 supported. -> 0.2.0.0+ Nov 8, 1:20 Refactored with linear, packer -> 0.3.0+
+ Graphics/Model/DirectX.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+-- | DirectX Object File (.x) Parser+module Graphics.Model.DirectX (+ loadXOF,+ parseXOF,+ reverseHand,++ XOF(..),+ DxMesh(..),+ DxMaterial(..)+ ) where++import Control.Applicative+import Data.Attoparsec.ByteString.Char8 as A+import qualified Data.ByteString as B+import Data.Traversable+import Data.Word+import Linear++-- | DirectX Object File+data XOF = XOF+ { _xofFormat :: B.ByteString+ , _xofTemplates :: [(B.ByteString, [B.ByteString])]+ , _xofHeader :: (Word16, Word16, Word32) -- ^ major minor flags+ , _xofMesh :: DxMesh+ -- , _xofFrames :: [(frame_name, [(mesh_name, DxMesh)])]+ -- , _xofAnimationSets :: [DxAnimationSet :: (frame_name, [DxKeyframe])]+ } deriving Show++data DxMesh = DxMesh+ { _dxVertices :: [V3 Float]+ , _dxFaces :: [Either (V4 Word32) (V3 Word32)]+ , _dxIndexes :: [Word32]+ , _dxMaterials :: [DxMaterial]+ , _dxNormals :: [V3 Float]+ , _dxNormalIndexes :: [Either (V4 Word32) (V3 Word32)]+ , _dxTexCoords :: [V2 Float]+ , _dxVertexColors :: [(Word32, V4 Float)]+ } deriving Show++data DxMaterial = DxMaterial+ { _dxFaceColor :: V4 Float+ , _dxPower :: Float+ , _dxSpecular :: V3 Float+ , _dxEmissive :: V3 Float+ , _dxTexturePath :: Maybe B.ByteString+ } deriving Show++--data DxKeyframe =+-- DxRotate Int Quaternion+-- | DxScale Int (V3 Float)+-- | DxTranslate Int (V3 Float)+-- | DxApplyMatrix Int M44 Float+++loadXOF :: FilePath -> IO XOF+loadXOF path = parseXOF <$> B.readFile path++parseXOF :: B.ByteString -> XOF+parseXOF bs =+ case feed (parse parseXMesh bs) "" of+ Done _ result -> result+ result -> error $ "parseXOF: Invalid format:\n" ++ show result++-- | Convert to right handed system.+reverseHand :: XOF -> XOF+reverseHand xof@XOF{_xofMesh=DxMesh{..}, ..} =+ xof { _xofMesh = (_xofMesh xof) {+ _dxVertices = fmap rhVtx _dxVertices,+ _dxFaces = fmap rhFace _dxFaces,+ _dxNormals = fmap rhVtx _dxNormals,+ _dxNormalIndexes = fmap rhFace _dxNormalIndexes+ }}+ where rhVtx (V3 x y z) = V3 x y (-z)+ rhFace (Right (V3 a b c)) = Right $ V3 b a c+ rhFace (Left (V4 a b c d)) = Left $ V4 a d c b++parseXMesh :: Parser XOF+parseXMesh = XOF+ <$> string "xof 0302txt 0064" <* skipSpace -- \r\n+ <*> many (templateDecl <* skipSpace)+ <*> headerDecl <* skipSpace+ <*> meshDecl++takeIdent :: Parser B.ByteString+takeIdent = A.takeWhile (inClass "a-zA-Z0-9_")++-- | e.g. template Header { field1; field2; ... }+templateDecl = try $+ (,) <$> (string "template" >> skipSpace >> takeIdent <* skipSpace) -- name+ <*> braces '{' '}' (skipSpace >> many (fieldDecl <* skipSpace)) -- fields++fieldDecl =+ braces '<' '>' (A.takeWhile (/= '>'))+ <|> braces '[' ']' (string "..." <|> takeIdent)+ <|> A.takeWhile (inClass "a-zA-Z0-9_ []") <* char ';'++float = realToFrac <$> signed double++getVector :: (Applicative a, Traversable a) => Parser (a Float)+getVector = sequenceA $ pure getFloatField++-- | e.g. <aaa> => braces '<' '>' (string "aaa")+braces l r p = char l *> p <* char r++semicolon = char ';' <* skipSpace+comma = char ','+maybeComma = (comma <|> return ' ') >> skipSpace++getIntField = decimal <* semicolon+getFloatField = float <* semicolon+getStringField = braces '"' '"' (A.takeWhile (/= '"')) <* semicolon++getVectorField :: (Applicative a, Traversable a) => Parser (a Float)+getVectorField = getVector <* semicolon++getArray :: Parser a -> Int -> Parser [a]+getArray parser len = count len (parser <* maybeComma) <* semicolon++section name p = do+ string name+ skipSpace+ braces '{' '}' (skipSpace >> p)++headerDecl :: Parser (Word16, Word16, Word32)+headerDecl = section "Header" $ do+ major <- fromIntegral <$> getIntField+ minor <- fromIntegral <$> getIntField+ flags <- fromIntegral <$> getIntField+ return (major, minor, flags)++meshDecl :: Parser DxMesh+meshDecl = section "Mesh" $ do+ vertices <- getIntField >>= getArray getVector+ faces <- getIntField >>= getArray meshFace+ (indexes, materials) <- option ([], []) meshMaterialList <* skipSpace+ (normals, faceNormals) <- option ([], []) meshNormals <* skipSpace+ DxMesh vertices faces indexes materials normals faceNormals+ <$> option [] meshTextureCoords <* skipSpace+ <*> option [] meshVertexColors <* skipSpace++meshFace = fmap Left meshFace4 <|> fmap Right meshFace3+meshFace4 = do+ char '4' >> semicolon+ sequenceA $ V4 (decimal <* comma) (decimal <* comma)+ (decimal <* comma) (decimal <* char ';')+meshFace3 = do+ char '3' >> semicolon+ sequenceA $ V3 (decimal <* comma)+ (decimal <* comma) (decimal <* char ';')++meshMaterialList = section "MeshMaterialList" $ do+ nMaterials <- getIntField+ indexes <- getIntField >>= getArray faceIndex+ semicolon+ materials <- count nMaterials (materialDecl <* skipSpace)+ return (indexes, materials)++faceIndex :: Parser Word32+faceIndex = decimal++materialDecl = section "Material" $ do+ faceColor <- getVectorField+ power <- getFloatField+ specularColor <- getVectorField+ emissiveColor <- getVectorField+ texFile <- optional textureFilename+ skipSpace+ return $ DxMaterial faceColor power specularColor emissiveColor texFile++textureFilename = section "TextureFilename" getStringField++meshNormals = section "MeshNormals" $ do+ normals <- getIntField >>= getArray getVector+ faceNormals <- getIntField >>= getArray meshFace+ return (normals, faceNormals)++meshTextureCoords = section "MeshTextureCoords" $+ getIntField >>= getArray getVector++meshVertexColors = section "MeshVertexColors" $+ getIntField >>= getArray indexedColor++indexedColor = (,) <$> fmap fromIntegral getIntField <*> getVector+
+ Graphics/Model/MikuMikuDance.hs view
@@ -0,0 +1,19 @@+-- | MikuMikuDance (MMD) model/motion reader.+-- This module provides unpacking interfaces for MMD 3D models and their motions,+-- both traditional PMD and newer PMX format are fully supported.+-- +-- > model <- loadMMD "博麗霊夢_n+式/博麗霊夢_n+式.pmd" :: IO MMD+-- +-- > motion <- loadVMD "Motion_BadApple/BadApple_Rside_20110206.vmd" :: IO VMD+module Graphics.Model.MikuMikuDance (+ loadMMD,+ decodeMMD,+ unpackMMD,+ loadVMD,+ decodeVMD,+ unpackVMD,+ module Graphics.Model.MikuMikuDance.Types+) where+import Graphics.Model.MikuMikuDance.Types+import Graphics.Model.MikuMikuDance.Loader+
+ Graphics/Model/MikuMikuDance/Loader.hs view
@@ -0,0 +1,530 @@+{-# LANGUAGE OverloadedStrings #-}+module Graphics.Model.MikuMikuDance.Loader where+import Control.Applicative+import Control.Monad+import Data.Int+import Data.Bits+import Data.Packer+import qualified Data.ByteString as B+import Graphics.Model.MikuMikuDance.Types+import Linear+import Unsafe.Coerce+++getFloat :: Unpacking Float -- Performance hack (may not work on your devices!)+getFloat = unsafeCoerce <$> getWord32LE++get2 :: Unpacking a -> Unpacking (V2 a)+get2 get = V2 <$> get <*> get++get3 :: Unpacking a -> Unpacking (V3 a)+get3 get = V3 <$> get <*> get <*> get++get4 :: Unpacking a -> Unpacking (V4 a)+get4 get = V4 <$> get <*> get <*> get <*> get++getV2 :: Unpacking (V2 Float)+getV2 = get2 getFloat++getV3 :: Unpacking (V3 Float)+getV3 = get3 getFloat++getV4 :: Unpacking (V4 Float)+getV4 = get4 getFloat++getWord8Int, getWord16Int, getWord32Int :: Unpacking Int+getWord8Int = fromIntegral <$> getWord8+getWord16Int = fromIntegral <$> getWord16LE+getWord32Int = fromIntegral <$> getWord32LE++-- Left handed system -> Right handed system+ltrTrans (V3 x y z) = V3 x y (-z)+ltrEular (V3 x y z) = V3 (-x) (-y) z+ltrQuat (V4 t i j k) = V4 (-t) i j k+getPos = fmap ltrTrans getV3+getEular = fmap ltrEular getV3+getQuat = fmap ltrQuat getV4+++-- * PMX++loadMMD :: FilePath -> IO MMD +loadMMD path = decodeMMD <$> B.readFile path++decodeMMD :: B.ByteString -> MMD+decodeMMD bs = runUnpacking unpackMMD bs++unpackMMD :: Unpacking MMD+unpackMMD = do+ magic <- getWord32BE+ case magic of+ 0x506d6400 -> unpackPMD+ 0x504d5820 -> unpackPMX+ _ -> fail "Not a MMD Model"++getUnsignedIndexUnpacker :: Unpacking (Unpacking Int32)+getUnsignedIndexUnpacker = do+ size <- getWord8+ return $ case size of+ 1 -> fromIntegral <$> getWord8+ 2 -> fromIntegral <$> getWord16LE+ 4 -> fromIntegral <$> getWord32LE++getIndexUnpacker :: Unpacking (Unpacking Int32)+getIndexUnpacker = do+ size <- getWord8+ return $ case size of+ 1 -> ((fromIntegral :: Int8 -> Int32) . fromIntegral) <$> getWord8+ 2 -> ((fromIntegral :: Int16 -> Int32) . fromIntegral) <$> getWord16LE+ 4 -> fromIntegral <$> getWord32LE++getTextBuf :: Unpacking B.ByteString+getTextBuf = getWord32Int >>= getBytesCopy++-- Polygon Model Extended+unpackPMX :: Unpacking MMD+unpackPMX = do+ version <- getFloat+ size <- getWord8+ when ((version /= 2.0 && version /= 2.1) || size /= 8) $+ fail "Not PMX 2.0/2.1"+ + -- PMX Header+ -- 0: UTF-16, 1: UTF-8+ isUTF16 <- getWord8+ let charset 0 = "UTF-16"+ charset 1 = "UTF-8"+ numExtUVs <- getWord8+ getVertexIndex <- getUnsignedIndexUnpacker+ getTextureIndex <- getIndexUnpacker+ getMaterialndex <- getIndexUnpacker+ getBoneIndex <- getIndexUnpacker+ getMorphIndex <- getIndexUnpacker+ getBodyIndex <- getIndexUnpacker+ modelName <- getTextBuf+ modelNameEn <- getTextBuf+ comment <- getTextBuf+ commentEn <- getTextBuf++ -- Vertex+ numVertices <- getWord32Int+ vertices <- replicateM numVertices $ do+ pos <- getPos+ normal <- getPos+ uv <- getV2+ extV4s <- replicateM (fromIntegral numExtUVs) getV4+ weightFormat <- getWord8+ w <- case weightFormat of+ 0 -> BDEF1 <$> getBoneIndex+ 1 -> BDEF2 <$> get2 getBoneIndex <*> getFloat+ 2 -> BDEF4 <$> get4 getBoneIndex <*> getV4+ -- SDEF need left<->right handed system transformation?+ 3 -> SDEF <$> get2 getBoneIndex <*> getFloat <*> get3 getPos+ 4 -> QDEF <$> get4 getBoneIndex <*> getV4+ -- edgeScalingFactor+ esf <- getFloat+ return $ MMDVertex pos normal uv extV4s w esf++ -- Plane+ numPlanes <- getWord32Int+ planes <- replicateM (numPlanes `div` 3) $ do+ V3 p q r <- get3 getVertexIndex+ return $ V3 q p r -- from left to right+ + -- Texture+ numTextures <- getWord32Int+ textures <- replicateM numTextures getTextBuf+ + -- Material+ numMaterials <- getWord32Int+ materials <- replicateM numMaterials $ do+ name <- getTextBuf+ nameEn <- getTextBuf+ diffuse <- getV4+ specular <- getV3+ shininess <- getFloat+ ambient <- getV3+ renderFlags <- getWord8+ edgeColor <- getV4+ edgeSize <- getFloat+ tex <- getTextureIndex+ sphereTex <- getTextureIndex+ sphereMode <- getWord8+ toonFlag <- getWord8+ toon <- case toonFlag of+ 0 -> getTextureIndex+ 1 -> (\x -> fromIntegral x - 10) <$> getWord8+ script <- getTextBuf+ verticesCount <- getWord32LE+ return $ MMDMaterial name nameEn diffuse specular shininess ambient+ renderFlags edgeColor edgeSize tex sphereTex sphereMode toon+ script verticesCount++ -- Bone+ numBones <- getWord32Int+ bones <- replicateM numBones $ do+ name <- getTextBuf+ nameEn <- getTextBuf+ pos <- getPos+ parent <- getBoneIndex+ transDepth <- getWord32Int+ flags <- getWord16LE+ link <- if flags .&. 1 == 0+ then Left <$> getV3+ else Right <$> getBoneIndex+ following <- if flags .&. 0x300 /= 0+ then Just <$> ((,) <$> getBoneIndex <*> getFloat)+ else return Nothing+ axis <- if flags .&. 0x400 /= 0+ then Just <$> getV3+ else return Nothing+ local <- if flags .&. 0x800 /= 0+ then Just <$> ((,) <$> getPos <*> getPos)+ else return Nothing+ external <- if flags .&. 0x2000 /= 0+ then Just <$> getWord32Int+ else return Nothing+ ik <- if flags .&. 0x20 /= 0+ then do+ ix <- getBoneIndex+ lc <- getWord32Int+ maxRot <- getFloat+ numlink <- getWord32Int+ links <- replicateM numlink $ do+ ix <- getBoneIndex+ ranged <- getWord8+ limit <- if ranged == 1+ then Just <$> ((,) <$> getEular <*> getEular)+ else return Nothing+ return (ix, limit)+ return . Just $ MMDIK ix lc maxRot links+ else return Nothing+ return $ MMDBone name nameEn pos parent transDepth flags link+ following axis local external ik++ -- Morph+ numMorphs <- getWord32Int+ morphs <- replicateM numMorphs $ do+ name <- getTextBuf+ nameEn <- getTextBuf+ facial <- getWord8+ morphType <- getWord8+ let getMorphOffset = case morphType of+ 0 -> GroupMorph <$> getMorphIndex <*> getFloat+ 1 -> VertexMorph <$> getVertexIndex <*> getPos+ 2 -> BoneMorph <$> getBoneIndex <*> getPos <*> getQuat+ 3 -> UVMorph <$> getVertexIndex <*> getV4+ 4 -> UV1Morph <$> getVertexIndex <*> getV4+ 5 -> UV2Morph <$> getVertexIndex <*> getV4+ 6 -> UV3Morph <$> getVertexIndex <*> getV4+ 7 -> UV4Morph <$> getVertexIndex <*> getV4+ 8 -> MaterialMorph <$> getMaterialndex <*> getWord8+ <*> getV4 <*> getV3 <*> getFloat+ <*> getV3 <*> getV4 <*> getFloat+ <*> getV4 <*> getV4 <*> getV4+ 9 -> FlipMorph <$> getMorphIndex <*> getFloat+ 10 -> ImpulseMorph <$> getBodyIndex <*> ((/= 0) <$> getWord8)+ <*> getPos <*> getEular+ numOffsets <- getWord32Int+ offsets <- replicateM numOffsets getMorphOffset+ return $ MMDMorph name nameEn facial offsets++ -- Group+ numGroups <- getWord32Int+ groups <- replicateM numGroups $ do+ name <- getTextBuf+ nameEn <- getTextBuf+ isSpecial <- getWord8+ count <- getWord32Int+ elements <- replicateM count $ do+ kind <- getWord8+ case kind of+ 0 -> Left <$> getBoneIndex+ 1 -> Right <$> getMorphIndex+ return $ MMDGroup name nameEn (isSpecial /= 0) elements++ -- Rigid Body+ numRigidBodies <- getWord32Int+ bodies <- replicateM numRigidBodies $+ MMDRigidBody <$> getTextBuf <*> getTextBuf <*> getBoneIndex+ <*> getWord8 <*> getWord16LE <*> getWord8+ <*> getV3 <*> getPos <*> getEular+ <*> getFloat <*> getFloat <*> getFloat+ <*> getFloat <*> getFloat <*> getWord8++ -- Joint+ numJoints <- getWord32Int+ joints <- replicateM numJoints $+ MMDJoint <$> getTextBuf <*> getTextBuf <*> getWord8+ <*> getBodyIndex <*> getBodyIndex+ <*> getPos <*> getEular+ <*> getPos <*> getPos <*> getEular <*> getEular+ <*> getPos <*> getEular++ -- Soft Body+ numSoftBodies <- getWord32Int+ softBodies <- replicateM numSoftBodies $+ MMDSoftBody <$> getTextBuf <*> getTextBuf <*> getWord8+ <*> getMaterialndex <*> getWord8 <*> getWord16LE+ <*> getWord8 <*> getWord32Int <*> getWord32Int+ <*> getFloat <*> getFloat <*> getWord32Int+ <*> get3 getV4 <*> get2 getV3+ <*> get4 getWord32Int <*> getV3+ <*> (getWord32Int >>= flip replicateM (+ (,,) <$> getBodyIndex <*> getVertexIndex+ <*> ((== 1) <$> getWord8)))+ <*> (getWord32Int >>= flip replicateM getVertexIndex)++ -- Sums up!+ return $ MMD version (charset isUTF16) numExtUVs+ modelName modelNameEn comment commentEn+ vertices planes textures [] materials bones morphs groups bodies+ joints softBodies++-- get fixed length C string terminating NUL byte+getFixedStr :: Int -> Unpacking B.ByteString+getFixedStr l = B.takeWhile (/= 0) <$> getBytesCopy l++-- Polygon Model Data+unpackPMD :: Unpacking MMD+unpackPMD = do+ unpackSetPosition 3+ version <- getFloat+ when (version /= 1.0) $+ fail "Not PMD 1.0"+ + -- PMD Header+ name <- getFixedStr 20+ comment <- getFixedStr 256+ + -- Vertex+ numVertices <- getWord32Int+ vertices <- replicateM numVertices $ do+ v <- MMDVertex <$> getPos <*> getPos <*> getV2+ weight <- BDEF2 <$> get2 getBoneIndex <*> getPercent+ edgeFlag <- getWord8+ let edgeScale = fromIntegral (1 - edgeFlag)+ return $ v [] weight edgeScale++ -- Plane+ numPlanesX3 <- getWord32Int+ planes <- replicateM (numPlanesX3 `div` 3) $ do+ V3 p q r <- get3 getVertexIndex+ return $ V3 q p r++ -- Material+ numMaterials <- getWord32Int+ materials <- forM [0 .. numMaterials - 1] $ \i -> do+ diffuse <- getV4+ specularity <- getFloat+ specular <- getV3+ ambient <- getV3+ toonIx <- fromIntegral <$> getWord8 -- -1:0.bmp, 0..9:[1..10].bmp+ edgeFlag <- getWord8 -- 1 to disable+ numVertices <- getWord32LE+ texturePath <- getFixedStr 20+ let sphereMode =+ -- "tex.bmp*sphere.sph" => Multiply (MMD 5.12-)+ -- "tex.bmp*sphere.spa" => Plus (MMD 5.12-)+ -- "tex.bmp/sphere.sph" => Multiply (MMD 5.11)+ -- "tex.bmp" or "sphere.sph" => None (MMD 5.09-)+ if 42 `B.elem` texturePath -- or 47 `elem` texturePath+ then if ".spa" `B.isSuffixOf` texturePath+ then 2 -- Add+ else 1 -- Mul+ else 0 -- None+ let i' = fromIntegral i+ return $ MMDMaterial texturePath ""+ diffuse specular specularity ambient+ ((1 - edgeFlag) * 16) -- draw edge+ (V4 0 0 0 1) 1.0 (i' * 2) (i' * 2 + 1) sphereMode+ (toonIx - 10) B.empty numVertices++ -- Texture+ let split x y | mSphereMode x == 0 = mNameJa x : mNameJa x : y+ split x y = t1 : B.tail t2 : y+ where (t1,t2) = B.break (== 42) (mNameJa x)+ let textures = foldr split [] materials+ + -- Bone+ numBones <- getWord16Int+ bones <- replicateM numBones $ do+ name <- getFixedStr 20+ parent <- getBoneIndex+ tailBone <- getBoneIndex+ -- 0:回転 1:回転と移動 2:IK 3:不明 4:IK影響下+ -- 5:回転影響下 6:IK接続先 7:非表示 8:捻り 9:回転運動+ typ <- getWord8+ ik <- getBoneIndex+ headPos <- getPos+ return $ MMDBone name "" headPos parent+ 0 0 -- XXX ?+ (Right 0) Nothing --XXX follow link+ Nothing Nothing Nothing Nothing++ -- IK+ numIKBones <- getWord16Int+ ikbones <- replicateM numIKBones $ do+ bone <- getBoneIndex+ target <- getBoneIndex+ ikLen <- getWord8Int+ iter <- getWord16Int+ rotLimit <- (* 4) <$> getFloat+ links <- replicateM ikLen $+ (\x -> (x, Nothing)) <$> getBoneIndex+ return $ (bone, MMDIK target iter rotLimit links)+ let bones' = zipWith (\ i bone ->+ bone { bIK = lookup i ikbones }) [0..] bones++ -- Face+ numFaces <- getWord16Int+ faces <- replicateM numFaces $ do+ name <- getFixedStr 20+ numVertices <- getWord32Int+ facial <- getWord8+ -- 0:Base, 1:Brow, 2: Eye, 3: Lip, 4:Other+ -- XXX 'base' should be ignored (0,0,0)???+ vMorph <- replicateM numVertices $+ VertexMorph <$> fmap fromIntegral getWord32LE <*> getPos+ return $ MMDMorph name "" facial vMorph+ + faceListLen <- getWord8Int+ faceList <- replicateM faceListLen getMorphIndex++ -- Group+ boneGroupsLen <- getWord8Int+ boneGroups <- replicateM boneGroupsLen (getFixedStr 50)+ let bGsEn = replicate boneGroupsLen ""+ numGroupedBones <- getWord32Int+ groupedBones <- replicateM numGroupedBones $+ (,) <$> getBoneIndex <*> getWord8++ -- Ext - English support since MMD 4.03+ empty <- isEmpty+ haveEnNames <- if empty then return False else (== 1) <$> getWord8+ (nameEn, commentEn, bones, faces, bGsEn) <-+ if not haveEnNames+ then return ("", "", bones, faces, bGsEn)+ else do+ name' <- getFixedStr 20+ comment' <- getFixedStr 256+ bones' <- forM bones $ \bone -> do+ nameEn <- getFixedStr 20+ return bone { bName = nameEn }+ faces' <- forM faces $ \morph -> do+ nameEn <- getFixedStr 20+ return morph { mphName = nameEn }+ bGsEn' <- replicateM (boneGroupsLen - 1) (getFixedStr 50)+ return (name', comment', bones', faces', bGsEn')++ -- UTF-16+ let root = "R\NULo\NULo\NULt\NUL"+ let rootGroup = MMDGroup root root True [Left 0]+ let faceGroup = MMDGroup "h\136\197`" "E\NULx\NULp\NUL" True+ (map Right faceList)+ let groups = rootGroup : faceGroup :+ zipWith3 (\i j e ->+ MMDGroup j e False $+ map (Left . fst) $+ filter ((== i).snd) groupedBones+ ) [1..] boneGroups bGsEn+ -- group 0 ('center') is reserved for the root bone+ + -- toon texture list since MMD 4.03+ toonTexList <- if empty then return []+ else replicateM 10 (getFixedStr 100)++ -- Ext - Bullet Physics+ -- Rigid Body+ numRigidBodies <- getWord32Int+ bodies <- replicateM numRigidBodies $+ MMDRigidBody <$> getFixedStr 20 <*> pure "" <*> getBoneIndex+ <*> getWord8 <*> getWord16LE <*> getWord8+ <*> getV3 <*> getPos <*> getEular+ <*> getFloat <*> getFloat <*> getFloat+ <*> getFloat <*> getFloat <*> getWord8+ -- Joint+ numJoints <- getWord32Int+ joints <- replicateM numJoints $+ MMDJoint <$> getFixedStr 20 <*> pure "" <*> pure 0+ <*> getBodyIndex <*> getBodyIndex+ <*> getPos <*> getEular+ <*> getPos <*> getPos <*> getEular <*> getEular+ <*> getPos <*> getEular+ + -- Sums up!+ return $ MMD 1 "Shift-JIS" 0 name nameEn comment commentEn+ vertices planes textures toonTexList materials bones'+ faces groups bodies joints []+ where+ toInt16 = fromIntegral+ toInt32 = fromIntegral :: Int16 -> Int32+ getBoneIndex = toInt32 . toInt16 <$> getWord16LE+ getPercent = (/100) . fromIntegral <$> getWord8+ getVertexIndex = fromIntegral <$> getWord16LE+ getMorphIndex = getVertexIndex+ getBodyIndex = fromIntegral <$> getWord32LE+++-- * VMD++loadVMD :: FilePath -> IO VMD+loadVMD path = decodeVMD <$> B.readFile path++decodeVMD :: B.ByteString -> VMD+decodeVMD bs = runUnpacking unpackVMD bs++unpackVMD :: Unpacking VMD+unpackVMD = do+ magic <- getBytes 30+ when (magic /= "Vocaloid Motion Data 0002\0\0\0\0\0") $+ -- MMD 3.0+ required.+ fail "Not Vocaloid Motion Data"+ + -- If the content is camera, light, and self shadow,+ -- model name should be "カメラ・照明\0on Data"+ name <- getFixedStr 20++ numBoneKeyframes <- getWord32Int+ boneM <- replicateM numBoneKeyframes $+ BoneKeyframe <$> getFixedStr 15 <*> getWord32LE <*> getPos+ <*> getQuat <*> replicateM 64 (fromIntegral <$> getWord8)+ + numMorphKeyframes <- getWord32Int+ morphM <- replicateM numMorphKeyframes $+ MorphKeyframe <$> getFixedStr 15 <*> getWord32LE <*> getFloat++ numCameraKeyframes <- getWord32Int+ cameraM <- replicateM numCameraKeyframes $+ CameraKeyframe <$> getWord32LE <*> getFloat+ <*> getPos <*> getEular+ <*> replicateM 24 (fromIntegral <$> getWord8)+ <*> getWord32LE <*> ((== 0) <$> getWord8)++ numLightKeyframes <- getWord32Int+ lightM <- replicateM numLightKeyframes $+ LightKeyframe <$> getWord32LE <*> getV3 <*> getPos++ -- After MMDv6.19 --+ empty <- isEmpty+ shadowM <- if empty then return []+ else do+ numSelfShadowKeyframes <- getWord32Int+ replicateM numSelfShadowKeyframes $+ ShadowKeyframe <$> getWord32LE+ <*> (fromIntegral <$> getWord8) <*> getFloat+ + -- Since MMDv7.40 --+ empty <- isEmpty+ ctrlM <- if empty then return []+ else do+ numCtrlKeyframes <- getWord32Int+ replicateM numCtrlKeyframes $+ ControlKeyframe <$> getWord32LE <*> ((== 1) <$> getWord8)+ <*> (getWord32Int >>= \count ->+ replicateM count+ ((,) <$> getFixedStr 20 <*> ((== 1) <$> getWord8)))++ -- Sums up!+ return $ VMD name boneM morphM cameraM lightM shadowM ctrlM+
+ Graphics/Model/MikuMikuDance/Types.hs view
@@ -0,0 +1,306 @@+module Graphics.Model.MikuMikuDance.Types (+ VertexIx,+ TexIx,+ BoneIx,+ MorphIx,+ MaterialIx,+ BodyIx,+ MMD(..),+ MMDVertex(..),+ MMDWeight(..),+ MMDMaterial(..),+ MMDBone(..),+ MMDIK(..),+ MMDMorph(..),+ MMDMorphOffest(..),+ MMDGroup(..),+ MMDRigidBody(..),+ MMDJoint(..),+ MMDSoftBody(..),++ VMD(..),+ BoneKeyframe(..),+ MorphKeyframe(..),+ CameraKeyframe(..),+ LightKeyframe(..),+ ShadowKeyframe(..),+ ControlKeyframe(..),++ VPD+ ) where+import qualified Data.ByteString as B+import Data.Int+import Data.Word+import Linear+++-- * PMX (Polygon Model eXtended)++type VertexIx = Int32+type TexIx = Int32+type BoneIx = Int32+type MorphIx = Int32+type MaterialIx = Int32+type BodyIx = Int32++-- | PMX 2.1 spec made by Kyockhook-P (極北P)+data MMD = MMD+ { mmdFormat :: Float+ -- ^ 1.0: PMD, 2.0/2.1:PMX+ , mmdCharCode :: String+ -- ^ UTF-16, UTF-8, Shift-JIS+ , mmdExtUVs :: Word8 -- ^ in [0..4]+ , mmdNameJa :: B.ByteString+ , mmdName :: B.ByteString+ , mmdCommentJa :: B.ByteString+ , mmdComment :: B.ByteString+ , mmdVertices :: [MMDVertex]+ , mmdPlanes :: [V3 VertexIx]+ -- ^ ABC(Triangle), /Since 2.1/: AAA(Point), ABA(Line)+ , mmdTextures :: [B.ByteString]+ , mmdAltToonTex :: [B.ByteString]+ -- ^ length: 0(PMX) or 10(PMD, if exists)+ , mmdMaterials :: [MMDMaterial]+ , mmdBones :: [MMDBone]+ , mmdMorphs :: [MMDMorph]+ , mmdGroups :: [MMDGroup]+ , mmdRigidBodies :: [MMDRigidBody]+ , mmdJoints :: [MMDJoint]+ , mmdSoftBodies :: [MMDSoftBody]+ -- ^ /Since 2.1/+ } deriving Show++data MMDVertex = MMDVertex+ { vPosition :: V3 Float+ , vNormal :: V3 Float+ , vUV :: V2 Float+ , vExtUVs :: [V4 Float]+ , vWeight :: MMDWeight+ , vEdgeScale :: Float+ } deriving Show++data MMDWeight =+ BDEF1 BoneIx+ -- | Weight of the first bone+ | BDEF2 (V2 BoneIx) Float+ -- | Weight of each bone+ | BDEF4 (V4 BoneIx) (V4 Float)+ -- | BDEF2 + SDEF-C(x,y,z) + SDEF-R0(x,y,z) + SDEF-R1(x,y,z)+ | SDEF (V2 BoneIx) Float (M33 Float)+ -- | /Since 2.1/ DualQuaternion+ | QDEF (V4 BoneIx) (V4 Float)+ deriving Show++data MMDMaterial = MMDMaterial+ { mNameJa :: B.ByteString+ , mName :: B.ByteString+ , mDiffuse :: V4 Float -- ^ RGBA+ , mSpecular :: V3 Float -- ^ RGB+ , mSpecularFactor :: Float+ , mAmbient :: V3 Float -- ^ RGB+ -- | 1:cullface=both, 2:drop shadow, 4:render to self shadow map,+ -- 8:render to self shadow, 16:draw edges,+ -- /Since 2.1/ => 32:vertex color, 64:As Points, 128:As Lines+ , mOptions :: Word8+ , mEdgeColor :: V4 Float -- ^ RGBA+ -- | /Since 2.1/ Point size (See mOptions)+ , mEdgeSize :: Float+ , mTexture :: TexIx+ , mSphereTexture :: TexIx+ -- | 0: None, 1: Multiply, 2: Add, 3: Use subtexture+ , mSphereMode :: Word8+ -- | Indexed texture >= 0, -10 <= shered toon texture (0-9) - 10 < 0+ , mToonTexture :: TexIx+ , mComment :: B.ByteString+ -- | The number of planes * 3+ , mNumVertices :: Word32+ } deriving Show++data MMDBone = MMDBone+ { bNameJa :: B.ByteString+ , bName :: B.ByteString+ , bPosition :: V3 Float+ , bParent :: BoneIx+ , bTransformDepth :: Int+ , bFlags :: Word16+ , bLInk :: Either (V3 Float) BoneIx -- ^ offset or bone index+ , bFollow :: Maybe (BoneIx, Float)+ , bAxis :: Maybe (V3 Float)+ , bLocalAxis :: Maybe (V3 Float, V3 Float)+ , bExternalParent :: Maybe Int+ , bIK :: Maybe MMDIK+ } deriving Show++data MMDIK = MMDIK+ { ikTarget :: BoneIx+ , ikLoopCount :: Int+ , ikRotLimitPerLoop :: Float+ , ikLinks :: [(BoneIx, Maybe (V3 Float, V3 Float))]+ } deriving Show++data MMDMorph = MMDMorph+ { mphNameJa :: B.ByteString+ , mphName :: B.ByteString+ , mphUICtrl :: Word8+ , mphOffsets :: [MMDMorphOffest]+ } deriving Show++data MMDMorphOffest =+ GroupMorph MorphIx Float+ | VertexMorph VertexIx (V3 Float) -- ^ offset+ | BoneMorph BoneIx (V3 Float) (V4 Float)+ | UVMorph VertexIx (V4 Float)+ | UV1Morph VertexIx (V4 Float)+ | UV2Morph VertexIx (V4 Float)+ | UV3Morph VertexIx (V4 Float)+ | UV4Morph VertexIx (V4 Float)+ | MaterialMorph MaterialIx Word8 (V4 Float) (V3 Float) Float (V3 Float) (V4 Float) Float (V4 Float) (V4 Float) (V4 Float)+ -- ^ 0:*, 1:+. Diffuse, Specular, SpecularFactor, Ambient, EdgeColor, EdgeSize, TextureFactors, SphereTexFactors, ToonTexFactors.+ | FlipMorph MorphIx Float+ -- ^ /Since 2.1/+ | ImpulseMorph BodyIx Bool (V3 Float) (V3 Float)+ -- ^ /Since 2.1/ isLocal, velocity, torque+ deriving Show++data MMDGroup = MMDGroup+ { gNameJa :: B.ByteString+ , gName :: B.ByteString+ , gIsReserved :: Bool+ , gElements :: [Either BoneIx MorphIx]+ } deriving Show++data MMDRigidBody = MMDRigidBody+ { rbNameJa :: B.ByteString+ , rbName :: B.ByteString+ , rbRelated :: BoneIx+ , rbGroup :: Word8+ , rbNoCollisionGroup :: Word16+ , rbShape :: Word8 -- ^ 0:Ball, 1:Box, 2:Capsule+ , rbSize :: V3 Float -- ^ (x,y,z)+ , rbPosition :: V3 Float -- ^ (x,y,z)+ , rbRotation :: V3 Float -- ^ (x,y,z)+ , rbMass :: Float+ , rbSpeedAttenuation :: Float+ , rbRotAttenuation :: Float+ , rbRepulsion :: Float+ , rbFriction :: Float+ , rbComputeMode :: Word8 -- ^ 0: static, 1: dynamic, 2: both+ } deriving Show++data MMDJoint = MMDJoint+ { jNameJa :: B.ByteString+ , jName :: B.ByteString+ , jointType :: Word8+ , jBone1 :: BodyIx+ , jBone2 :: BodyIx+ , jPosition :: V3 Float+ , jRotation :: V3 Float+ , jPosUpperBound :: V3 Float+ , jPosLowerBound :: V3 Float+ , jRotUpperBound :: V3 Float+ , jRotLowerBound :: V3 Float+ , jPosSpringConst :: V3 Float+ , jRotSpringConst :: V3 Float+ } deriving Show++-- | /Since 2.1/+data MMDSoftBody = MMDSoftBody+ { sbNameJa :: B.ByteString+ , sbName :: B.ByteString+ , sbShape :: Word8+ -- ^ 0:TriMesh, 1:Rope+ , sbRelated :: MaterialIx+ , sbGroup :: Word8+ , sbNoCollisionGroup :: Word16+ , sbOptions :: Word8+ -- ^ 1:generateBendingConstraints(B-Link), 2:generateClusters, 4:randomizeConstraints+ , sbBLinkDistance :: Int+ , sbNumClusters :: Int+ , sbMass :: Float+ , sbCollisionMargin :: Float+ , sbAeroModel :: Int+ -- ^ 0:V_Point, 1:V_TwoSided, 2:V_OneSided, 3:F_TwoSided, 4:F_OneSided+ , sbConfig :: V3 (V4 Float)+ -- ^ (VCF DP DG LF, PR VC DF MT, CHR KHR SHR AHR)+ , sbCluster :: V2 (V3 Float)+ -- ^ (SRHR_CL SKHR_CL SSHR_CL, SR_SPLT_CL SK_SPLT_CL SS_SPLT_CL)+ , sbIteration :: V4 Int+ -- ^ V_IT P_IT D_IT C_IT+ , sbMaterial :: V3 Float+ -- ^ LST AST VST+ , sbAnchorRigidBodies :: [(BodyIx, VertexIx, Bool)]+ -- ^ True: Enable Near mode+ , sbPinVertices :: [VertexIx]+ } deriving Show+++-- * VMD (Vocaloid Motion Data)++-- VMD strings are encoded in CP932+-- | Vocaloid Motion Data+data VMD = VMD+ { vmdModelName :: B.ByteString -- ^ CP932+ , boneMotion :: [BoneKeyframe]+ , morphMotion :: [MorphKeyframe]+ , cameraMotion :: [CameraKeyframe]+ , lightMotion :: [LightKeyframe]+ , shadowMotion :: [ShadowKeyframe]+ , controlMotion :: [ControlKeyframe]+ } deriving Show++data BoneKeyframe = BoneKeyframe+ { bkName :: B.ByteString+ , bkFrame :: Word32+ , bkPosition :: V3 Float -- ^ XYZ or (0,0,0)+ , bkRotation :: V4 Float -- ^ Quaternion XYZW or (0,0,0,1)+ , bkInterpolation :: [Int] -- XXX description+ } deriving Show++data MorphKeyframe = MorphKeyframe+ { moName :: B.ByteString+ , moFrame :: Word32+ , moFace :: Float+ } deriving Show++data CameraKeyframe = CameraKeyframe+ { ckFrame :: Word32+ , ckDistance :: Float+ , ckPosition :: V3 Float+ , ckRotation :: V3 Float+ , ckBezier :: [Int] -- ^ bezier int[24]+ , ckViewingAngle :: Word32 -- ^ in degree+ , ckPerspective :: Bool -- 0:Enable, 1:Disable+ } deriving Show++data LightKeyframe = LightKeyframe+ { lkFrame :: Word32+ , lkColor :: V3 Float+ , lkPosition :: V3 Float+ } deriving Show++data ShadowKeyframe = ShadowKeyframe+ { skFrame :: Word32+ , skType :: Int+ , skDistance :: Float -- ^ 0.1 - (dist * 0.00001)+ } deriving Show++data ControlKeyframe = ControlKeyframe+ { ctFrame :: Word32+ , ctModelVisibility :: Bool+ , ctIKCapability :: [(B.ByteString, Bool)]+ } deriving Show+++-- * VPD++-- | Vocaloid Pose Data+-- (name of a Bone, position of the bone, quaternion of the bone)+type VPD = (B.ByteString, V3 Float, V4 Float)+++-- * VAC++-- | Vocaloid Accessory +-- (name of an accessory, .x filename, position, eular angle, name of the bound bone)+type VAC = (String, FilePath, Float, V3 Float, V3 Float, String)+
+ Graphics/Model/Obj.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+-- | OBJ is a widely used simple 3D model format made for TAV.+-- The Advanced Visualizer (TAV), a 3D graphics software package,+-- was the flagship product of Wavefront Technologies from the 1980s+-- until the 1990s.+-- <https://en.wikipedia.org/wiki/The_Advanced_Visualizer>++module Graphics.Model.Obj (+ loadOBJ,+ parseOBJ,+ --reverseHandOBJ,+ loadMtl,+ parseMtl,++ ObjMesh(..),+ ObjFace(..),+ ObjMaterial(..)+ ) where++import Control.Applicative+import Data.Attoparsec.ByteString.Char8 as A+import qualified Data.ByteString as B+import Data.Int+import Linear++data ObjMesh = ObjMesh+ { _objGroup :: String+ , _objMaterial :: Maybe (FilePath, String)+ , _objVertices :: [V3 Float]+ , _objUVs :: [V2 Float]+ , _objNormals :: [V3 Float]+ , _objFaces :: [[ObjFace]]+ } deriving Show++data ObjFace = ObjFace+ { _objVertexIx :: Int32+ , _objTextureIx :: Int32+ , _objNormalIx :: Int32+ } deriving Show++data ObjMaterial = ObjMaterial+ { _objName :: String+ , _objAmbient :: V3 Float+ , _objDiffuse :: V3 Float+ , _objSpecular :: V3 Float+ , _objShineness :: Float+ } deriving Show++loadOBJ :: FilePath -> IO [ObjMesh]+loadOBJ path = parseOBJ <$> B.readFile path++parseOBJ :: B.ByteString -> [ObjMesh]+parseOBJ bs =+ case feed (parse parseMeshes bs) "" of+ Done _ result -> result+ result -> error $ "parseOBJ: Invalid format:\n" ++ show result++comments = do+ many (skipSpace >> char '#' >> A.takeWhile (/= '\n'))+ skipSpace++parseMeshes :: Parser [ObjMesh]+parseMeshes = do+ path <- comments *> option "" parseMtllib <* comments+ many1 (parseMesh path <* comments)++parseMesh :: String -> Parser ObjMesh+parseMesh path = do+ group <- option "default" parseG <* comments+ mtl1 <- optional parseUsemtl <* comments+ vertices <- many1 (parseV <* comments)+ uvs <- many (parseVt <* comments)+ normals <- many (parseVn <* comments)+ mtl2 <- optional parseUsemtl <* comments+ faces <- many (parseF <* comments)+ let mtl = fmap (path,) (maybe mtl2 Just mtl1)+ return $ ObjMesh group mtl vertices uvs normals faces++getString, parseMtllib, parseG, parseUsemtl :: Parser String+getString = many1 (satisfy (/= '\n'))+parseMtllib = string "mtllib " >> getString+parseG = char 'g' >> sp >> getString+parseUsemtl = string "usemtl " >> getString++parseV = char 'v' >> float3+parseVt = char 'v' >> char 't' >> float2+parseVn = char 'v' >> char 'n' >> float3+parseF = char 'f' >> many indexes++float = realToFrac <$> signed double+sp = char ' '+float3 = V3+ <$> (sp *> float)+ <*> (sp *> float)+ <*> (sp *> float)+float2 = V2+ <$> (sp *> float)+ <*> (sp *> float)++index = option 0 decimal+indexes = ObjFace+ <$> (sp *> index)+ <*> (char '/' *> index)+ <*> (char '/' *> index)++loadMtl :: FilePath -> IO ObjMaterial+loadMtl path = parseMtl <$> B.readFile path++parseMtl :: B.ByteString -> ObjMaterial+parseMtl bs =+ case feed (parse parseMtl' bs) "" of+ Done _ result -> result+ result -> error $ "parseMtl: Invalid format:\n" ++ show result++parseMtl' :: Parser ObjMaterial+parseMtl' = do+ comments+ mtl <- string "newmtl " >> getString <* comments+ amb <- string "Ka" >> float3 <* comments+ diff <- string "Kd" >> float3 <* comments+ spec <- string "Ks" >> float3 <* comments+ shine <- string "Ns " >> float+ -- XXX map_Kd+ return $ ObjMaterial mtl amb diff spec shine+
+ LICENSE view
@@ -0,0 +1,165 @@+ GNU LESSER GENERAL PUBLIC LICENSE+ Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.+++ This version of the GNU Lesser General Public License incorporates+the terms and conditions of version 3 of the GNU General Public+License, supplemented by the additional permissions listed below.++ 0. Additional Definitions.++ As used herein, "this License" refers to version 3 of the GNU Lesser+General Public License, and the "GNU GPL" refers to version 3 of the GNU+General Public License.++ "The Library" refers to a covered work governed by this License,+other than an Application or a Combined Work as defined below.++ An "Application" is any work that makes use of an interface provided+by the Library, but which is not otherwise based on the Library.+Defining a subclass of a class defined by the Library is deemed a mode+of using an interface provided by the Library.++ A "Combined Work" is a work produced by combining or linking an+Application with the Library. The particular version of the Library+with which the Combined Work was made is also called the "Linked+Version".++ The "Minimal Corresponding Source" for a Combined Work means the+Corresponding Source for the Combined Work, excluding any source code+for portions of the Combined Work that, considered in isolation, are+based on the Application, and not on the Linked Version.++ The "Corresponding Application Code" for a Combined Work means the+object code and/or source code for the Application, including any data+and utility programs needed for reproducing the Combined Work from the+Application, but excluding the System Libraries of the Combined Work.++ 1. Exception to Section 3 of the GNU GPL.++ You may convey a covered work under sections 3 and 4 of this License+without being bound by section 3 of the GNU GPL.++ 2. Conveying Modified Versions.++ If you modify a copy of the Library, and, in your modifications, a+facility refers to a function or data to be supplied by an Application+that uses the facility (other than as an argument passed when the+facility is invoked), then you may convey a copy of the modified+version:++ a) under this License, provided that you make a good faith effort to+ ensure that, in the event an Application does not supply the+ function or data, the facility still operates, and performs+ whatever part of its purpose remains meaningful, or++ b) under the GNU GPL, with none of the additional permissions of+ this License applicable to that copy.++ 3. Object Code Incorporating Material from Library Header Files.++ The object code form of an Application may incorporate material from+a header file that is part of the Library. You may convey such object+code under terms of your choice, provided that, if the incorporated+material is not limited to numerical parameters, data structure+layouts and accessors, or small macros, inline functions and templates+(ten or fewer lines in length), you do both of the following:++ a) Give prominent notice with each copy of the object code that the+ Library is used in it and that the Library and its use are+ covered by this License.++ b) Accompany the object code with a copy of the GNU GPL and this license+ document.++ 4. Combined Works.++ You may convey a Combined Work under terms of your choice that,+taken together, effectively do not restrict modification of the+portions of the Library contained in the Combined Work and reverse+engineering for debugging such modifications, if you also do each of+the following:++ a) Give prominent notice with each copy of the Combined Work that+ the Library is used in it and that the Library and its use are+ covered by this License.++ b) Accompany the Combined Work with a copy of the GNU GPL and this license+ document.++ c) For a Combined Work that displays copyright notices during+ execution, include the copyright notice for the Library among+ these notices, as well as a reference directing the user to the+ copies of the GNU GPL and this license document.++ d) Do one of the following:++ 0) Convey the Minimal Corresponding Source under the terms of this+ License, and the Corresponding Application Code in a form+ suitable for, and under terms that permit, the user to+ recombine or relink the Application with a modified version of+ the Linked Version to produce a modified Combined Work, in the+ manner specified by section 6 of the GNU GPL for conveying+ Corresponding Source.++ 1) Use a suitable shared library mechanism for linking with the+ Library. A suitable mechanism is one that (a) uses at run time+ a copy of the Library already present on the user's computer+ system, and (b) will operate properly with a modified version+ of the Library that is interface-compatible with the Linked+ Version.++ e) Provide Installation Information, but only if you would otherwise+ be required to provide such information under section 6 of the+ GNU GPL, and only to the extent that such information is+ necessary to install and execute a modified version of the+ Combined Work produced by recombining or relinking the+ Application with a modified version of the Linked Version. (If+ you use option 4d0, the Installation Information must accompany+ the Minimal Corresponding Source and Corresponding Application+ Code. If you use option 4d1, you must provide the Installation+ Information in the manner specified by section 6 of the GNU GPL+ for conveying Corresponding Source.)++ 5. Combined Libraries.++ You may place library facilities that are a work based on the+Library side by side in a single library together with other library+facilities that are not Applications and are not covered by this+License, and convey such a combined library under terms of your+choice, if you do both of the following:++ a) Accompany the combined library with a copy of the same work based+ on the Library, uncombined with any other library facilities,+ conveyed under the terms of this License.++ b) Give prominent notice with the combined library that part of it+ is a work based on the Library, and explaining where to find the+ accompanying uncombined form of the same work.++ 6. Revised Versions of the GNU Lesser General Public License.++ The Free Software Foundation may publish revised and/or new versions+of the GNU Lesser General Public License from time to time. Such new+versions will be similar in spirit to the present version, but may+differ in detail to address new problems or concerns.++ Each version is given a distinguishing version number. If the+Library as you received it specifies that a certain numbered version+of the GNU Lesser General Public License "or any later version"+applies to it, you have the option of following the terms and+conditions either of that published version or of any later version+published by the Free Software Foundation. If the Library as you+received it does not specify a version number of the GNU Lesser+General Public License, you may choose any version of the GNU Lesser+General Public License ever published by the Free Software Foundation.++ If the Library as you received it specifies that a proxy can decide+whether future versions of the GNU Lesser General Public License shall+apply, that proxy's public statement of acceptance of any version is+permanent authorization for you to choose that version for the+Library.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain