{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, TypeSynonymInstances #-} ---------------------------------------------------------------------- -- | -- Module : Graphics.SceneGraph.Basic -- Copyright : (c) Mark Wassell 2008 -- License : LGPL -- -- Maintainer : mwassell@bigpond.net.au -- Stability : experimental -- Portability : portable -- -- Definition of types and combinators. -- -- Construction of the graph is done within a Monad ('OSGT'). 'osg' is then used -- to extract the 'Scene'. -- -- -- ---------------------------------------------------------------------- module Graphics.SceneGraph.Basic ( SceneNode(..),SceneGraph,OSG,Scene,Colour(..),SceneData(..),Phong(..), Geometry(..),emptyOSG,OSGState(..),emptyState,emptyStateWithRef,newPhong, nullNode,trivialGr,addNode',addNullNode,addNodeBasic,addBasicNode,addBasicNamedNode, addNode,replaceNode,findCamera,findCameraPath,rotateX,rotateY,rotateZ, torus,sphere,tetra,line,cube,switchHandler,light,camera,plane,texture,planeQ,text, scaleS, scale, translate, rotate, (<+>),(<*>),(),strip,switch,switch', switchNode',translateSG',rotatePostSG',translatePostSG', colour,cylinder, osg,runOSG,r,llab,getTransformTo,findHandlerDown,findTextDown, OSGT, replaceNode'',handler,handler2,handleClickEvent,label,getByLabel, getHitAction,findHandler,replaceNode',OSGStateRef(..),SinkValue(..),dragHandler ) where import Data.Array hiding (bounds) import Data.IORef import Data.Graph.Inductive hiding (mkNode,context) import Data.Sequence hiding (empty,fromList) import Data.Tree import Data.List hiding (group,union) import qualified Data.Map as M import Foreign.Storable import Graphics.UI.GLUT.Objects as GL import Graphics.Rendering.OpenGL.GL.BeginEnd import Graphics.Rendering.OpenGL (Vector3) import Graphics.UI.GLUT hiding (Sink,Red, Green,Blue,Matrix,Error,get,scale,translate,rotate,Light,light,texture,Texture,Text,set,get) import Graphics.UI.GLUT.Fonts import Control.Monad.Identity import Control.Monad.Error import qualified Control.Monad.State as ST import Numeric.LinearAlgebra (Vector,toList,fromList,Matrix,mul,fromLists,toLists) import Graphics.SceneGraph.Matrix import Graphics.SceneGraph.Vector import Graphics.SceneGraph.Utils -- | Scene Graph based on a Graph type SceneGraph = Gr SceneNode () -- | Scene Node. Made up of data and maybe a widget data SceneNode = SceneNode (Node,String) SceneData deriving Show -- | Scene Graph with indicate root node type Scene = (SceneGraph,Node) type SceneRef = (IORef SceneGraph, Node) -- | View port refers to a camera node and has its own Scene which is drawn flattened data Viewport = Viewport Node Scene -- | A scene with a number of view ports looking onto it. type World = (Scene,[Viewport]) instance Eq SceneNode where (==) (SceneNode n _ ) (SceneNode m _ ) = (m == n) type ClickHandler = Scene -> KeyState -> IO SceneGraph type DragHandler = Scene -> Vector GLdouble -> IO (SceneGraph,GLdouble) instance Show ClickHandler where show _ = "" instance Show DragHandler where show _ = "" data SinkValue = SVD GLdouble | SVB Bool | SVT String type Sink a = a -> IO () -- | Scene Node Data. data SceneData = Group | Geode Geometry | LOD | MatrixTransform (MatrixD) | Switch Int | Material Phong | Handler (Maybe (ClickHandler, Sink ())) (Maybe (DragHandler, Sink GLdouble)) | Light | Camera | Texture String | Text String instance Show SceneData where show Group = "Group" show (Geode (GLObj o)) = "Geode" ++ (show o) show (Geode (BezierMesh _)) = "Geode BezierMesh" show (Geode _) = "Geode" show LOD = "LOD" show (MatrixTransform _) = "MatrixTransform" show (Switch i) = "Switch " ++ show i show (Material _) = "Material" show (Handler _ _) = "Handler" show Light = "Light" show Camera = "Camera" show (Texture _) = "Texture" show (Text t ) = "Text " ++ t type Value = Either Int String type PropMap = M.Map String Value getRootNode :: SceneRef -> IO SceneNode getRootNode (ref, nde) = do sg <- readIORef ref return $ llab sg nde -- | Geometry. Either a basic GL object or a mesh. -- -- FIXME - Reduce number of mesh types - to whatever is easier to draw (I suppose) data Geometry = GLObj Object | Mesh1 [(PrimitiveMode, Int, Int)] [VectorD] [VectorD] | Mesh2 [(PrimitiveMode, Int, Int)] (Array Int (VectorD, VectorD, Maybe (VectorD))) | Mesh3 [VectorD] | BezierMesh [[[Vertex3 GLfloat]]] deriving (Eq,Show) instance Eq SceneGraph where (==) a b = equal a b -- | Simple colours data Colour = Grey |JustWhite |Red | Green | Blue | Black | LightBlue | White | Yellow deriving (Show,Eq) -- | Phong colouring data Phong = Phong { emissionPh :: Maybe (Color4 GLfloat), ambientPh :: Maybe (Color4 GLfloat), diffusePh :: Maybe (Color4 GLfloat), specularPh :: Maybe (Color4 GLfloat), shinePh :: Maybe (GLfloat), reflectivePh :: Maybe (Color4 GLfloat), reflectivityPh :: Maybe (GLfloat), transparentPh :: Maybe (Color4 GLfloat), tranparencyPh :: Maybe (GLfloat) } deriving (Eq,Show) newPhong = Phong Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing -- | Convert from simple colour to Phong colour2Phong :: Colour -> Phong colour2Phong c = newPhong { diffusePh = Just $ mapColour c, ambientPh = Just $ mapColour c, specularPh = Just $ Color4 0.4 0.4 0.4 1.0, shinePh = Just 5.0 } -- | Creates an empty scene graph nullNode n = SceneNode (n,show n) Group -- | Creates a scene graph containing the supplied node trivialGr :: SceneNode -> SceneGraph trivialGr n = ([],1,n,[]) & empty data Throwable = ThrowError String deriving Show instance Error Throwable where noMsg = ThrowError "An Error" strMsg s = ThrowError s newtype OSGStateRef = OSGStateRef { getOSR :: IORef OSGState } instance Show OSGStateRef where show x = "StateRef" instance Eq OSGStateRef where (==) a b = True -- | Holds state of graph as it is built. data OSGState = OSGState { gr :: SceneGraph , context :: [SceneNode] , heap :: M.Map Int SceneNode , startNode :: Int , root::Int , selfRef :: Maybe OSGStateRef } deriving (Eq,Show) -- | Empty state emptyState = OSGState { gr = empty, context = [], heap = M.empty,startNode = 0,root=0, selfRef = Nothing } -- | Empty state with the self reference set emptyStateWithRef :: IO OSGState emptyStateWithRef = do r <- newIORef emptyState let state = (emptyState { selfRef = Just $ OSGStateRef r }) writeIORef r state return state -- | The OSG monad within which construction of scene graphs occur. -- was 'type OSGT m = ErrorT Throwable (ST.StateT OSGState m)' type OSGT m = (ST.StateT OSGState m) type OSG = OSGT Identity -- | Basic add node addNodeBasic :: Monad m => SceneNode -> OSGT m SceneNode addNodeBasic nde = addNode nde [] -- | Add node with scene data addBasicNode g = addNode (SceneNode (0,"") g ) [] -- | Add node with scene data addBasicNamedNode name g = addNode (SceneNode (0,name) g ) [] -- | Add empty node addNullNode :: Monad m => OSGT m SceneNode addNullNode = addNodeBasic $ nullNode 0 -- | Add a node to a scene graph with supplied children addNode :: Monad m => SceneNode -> [((),Node)] -> OSGT m SceneNode addNode nde children = do s <- ST.get let (sn,s') = addNode' s nde children ST.put s' return sn -- | Non-monadic form of addNode addNode' :: OSGState -> SceneNode -> [((),Node)] -> (SceneNode,OSGState) addNode' s (SceneNode (m,l) d ) children = let n = if m == 0 then (startNode s) + 2 else m sn = SceneNode (n,l) d g' = ([],n,sn,children) & (gr s) s' = s { gr = g', startNode = n,root=n } in (sn,s') -- | Replace a Scene Node replaceNode :: Monad m => SceneNode -> OSGT m SceneNode replaceNode n = do s <- ST.get g' <- lift $ replaceNode' (gr s) n ST.put (s { gr = g' }) return n -- | Inner monad version of replace node replaceNode' :: Monad md => SceneGraph -> SceneNode -> md SceneGraph replaceNode' gr nn = return $ replaceNode'' gr nn -- | Actually does the job of replacing node in a scene graph replaceNode'' :: SceneGraph -> SceneNode -> SceneGraph replaceNode'' gr nn = let (m,gr') = match (idd nn) gr in case m of Nothing -> gr Just (i,n,_,o) -> (i,n,nn,o) & gr' -- | Run the monad but keep it in the family. runOSGL :: Monad m => OSGState -> OSGT m SceneNode -> OSGT m (SceneNode,OSGState,Node) runOSGL s n = lift $ runOSG s n -- | Run the monad but keep it in the family. runOSGL' :: Monad m => OSGT m SceneNode -> OSGT m (SceneNode,Node) runOSGL' n = do s <- ST.get (n1,s',i) <- runOSGL s n ST.put s' return (n1,i) -- | Perform a function on a scene node doOnNode :: Monad m => OSGT m SceneNode -> (SceneNode -> SceneNode ) -> OSGT m SceneNode doOnNode n f = do s <- ST.get (anode,s',i) <- runOSGL s n ST.put s' replaceNode (f anode) -- | Create a light light :: Monad m => OSGT m SceneNode light = addBasicNode Light -- | Create a camera camera :: Monad m => OSGT m SceneNode camera = addBasicNode Camera fi = fromIntegral plane' :: Int -> ([(PrimitiveMode, Int, Int)],[VectorD],[VectorD]) plane' w = foldr (\ (a1,a2,a3) (b1,b2,b3) -> (a1:b1,a2++b2,a3++b3)) ([],[],[]) [ up w xs | xs <- [0..(w-1)]] up :: Int -> Int -> ( (PrimitiveMode,Int,Int), [VectorD],[VectorD]) up w xs = ( (TriangleStrip, (xs*(w*2+2))+1, w*2+2), [ fromList [(fi x),(fi y),0] | y <- [0..w],x <-[xs..(xs+1)]], [ fromList [0,0,1] | x <-[1..2], y <- [0..w]]) -- | Create a plane planeT :: Monad m => Int -> OSGT m SceneNode planeT w = addBasicNode (Geode $ Mesh1 a b c) where (a,b,c) = plane' w -- | Create a quad mesh quad :: (Int,Int) -> ([(PrimitiveMode, Int, Int)],[VectorD],[VectorD]) quad (x,y) = ( [(Quads,1,100) ], [ fromList[x',y',0], fromList[(x'+1),y',0], fromList[(x'+1),(y'+1),0], fromList [x',(y'+1),0]], [fromList [0,0,1] | i <- [0..3]]) where x' = fi x y' = fi y planeq' w = foldr (\ (a1,a2,a3) (b1,b2,b3) -> (a1,a2++b2,a3++b3)) ([],[],[]) [ quad (x,y) | x <- [0..(w-1)], y <- [0..(w-1)]] plane :: Monad m => Int -> OSGT m SceneNode plane w = addBasicNode (Geode $ Mesh1 a b c) where (a,b,c) = planeq' w planeQ :: Monad m => Int -> OSGT m SceneNode planeQ = plane -- | Create a node containing a torus. torus :: Monad m => Float -> OSGT m SceneNode torus i = addBasicNode (Geode $ GLObj $ GL.Torus (realToFrac i) (realToFrac (r*2)) 50 50) -- | Create a node containing a sphere sphere :: Monad m => Float -> OSGT m SceneNode sphere r = addBasicNode (Geode $ GLObj $ GL.Sphere' (realToFrac r) 50 50) -- | Create a node containing a tetrahedron tetra :: Monad m => OSGT m SceneNode tetra = addBasicNode (Geode $ GLObj $ GL.Tetrahedron) -- | Create a node containing a line line :: Monad m => VectorD -> VectorD -> OSGT m SceneNode line p q = addBasicNode (Geode $ Mesh1 [(Lines,1,2)] [p,q] [v1,v1] ) -- | Create a node containing a cube. -- Fixme: Faces are not orientated same way. cube :: Monad m => GLdouble -> OSGT m SceneNode cube i = addBasicNode (Geode $ Mesh1 [ (Quads,1,6) ] (map fromList [ [md,md,md], [d,md,md], [d,d,md], [md,d,md], -- Z [md,d,d], [d,d,d], [d,md,d], [md,md,d], [d,md,md], [d,d,md], [d,d,d],[d,md,d], -- X [md,md,d], [md,d,d], [md,d,md],[md,md,md], [md,d,md], [d,d,md], [d,d,d], [md,d,d], -- Y [md,md,md], [d,md,md], [d,md,d], [md,md,d] ]) (map fromList [ [0,0,1], [0,0,1], [0,0,1], [0,0,1], [0,0,mu], [0,0,mu], [0,0,mu], [0,0,mu], [mu,0,0], [mu,0,0], [mu,0,0], [mu,0,0], [1,0,0], [1,0,0], [1,0,0], [1,0,0], [0,mu,0], [0,mu,0],[0,mu,0],[0,mu,0], [0,1,0],[0,1,0],[0,1,0],[0,1,0] ])) where (d,md,mu) = (i/2,(-i/2),(-1)) -- | Create cylinder as a BezierMesh cylinder :: Monad m => GLfloat -> GLfloat -> OSGT m SceneNode cylinder r h = addBasicNode $ Geode $ BezierMesh $ [ [ (let z=z'*h in [Vertex3 0 (-r) z, Vertex3 (-d) (-r) z, Vertex3 (-r) (-d) z, Vertex3 (-r) 0 z ]) | z' <- [0..1]], [ (let z=z'*h in[Vertex3 (-r) 0 z, Vertex3 (-r) d z , Vertex3 (-d) r z , Vertex3 0 r z ]) | z' <- [0..1]] , [ (let z=z'*h in[Vertex3 0 r z , Vertex3 d r z , Vertex3 r d z , Vertex3 r 0 z ]) | z' <- [0..1]], [ (let z=z'*h in[Vertex3 r 0 z , Vertex3 r (-d) z , Vertex3 d (-r) z , Vertex3 0 (-r) z ]) | z' <- [0..1]]] where d = 0.66 * r -- | Scale a node by equal amounts in all directions scaleS :: Monad m => OSGT m SceneNode -> GLdouble -> OSGT m SceneNode scaleS n f = scale n (vector3 f f f) -- | Scale a node scale :: Monad m => OSGT m SceneNode -> VectorD -> OSGT m SceneNode scale n v = transformSG n (\x -> scaleM v x) (\x -> scale x v) -- | Translate a node translate :: Monad m => OSGT m SceneNode -> VectorD -> OSGT m SceneNode translate n v = transformSG n (translateM v) ((flip translate) v) -- | Rotate a node by an angle around a vector. rotate :: Monad m => OSGT m SceneNode -> (GLdouble,VectorD) -> OSGT m SceneNode rotate n a@(theta,v) = transformSG n (\x -> rotateM theta v x) (\x -> rotate x a) rad x = x * pi /180 -- | Rotate a node around X axis rotateX :: Monad m => OSGT m SceneNode -> GLdouble -> OSGT m SceneNode rotateX n theta= rotate n ( (rad theta),vector3 1 0 0 ) -- | Rotate a node around Y axis rotateY :: Monad m => OSGT m SceneNode -> GLdouble -> OSGT m SceneNode rotateY n theta= rotate n ( (rad theta),vector3 0 1 0 ) -- | Rotate a node around Z axis rotateZ :: Monad m => OSGT m SceneNode -> GLdouble -> OSGT m SceneNode rotateZ n theta= rotate n ( (rad theta),vector3 0 0 1 ) -- | Apply colour to the node colourSG :: Monad m => OSGT m SceneNode -> (Phong -> Phong) -> (OSGT m SceneNode -> OSGT m SceneNode ) -> OSGT m SceneNode colourSG n action self = do (n1,i) <- runOSGL' n case n1 of (SceneNode n (Material p)) -> do let p' = action p replaceNode (SceneNode n (Material p')) sn -> do let n'' = addNode (SceneNode (0,"") (Material newPhong)) [((),i)] self n'' -- | Transform the node of a scene graph within the Monad with the supplied matrix transform transformSG :: Monad m => OSGT m SceneNode -> (MatrixD -> MatrixD) -> (OSGT m SceneNode -> OSGT m SceneNode ) -> OSGT m SceneNode transformSG n action self = do (n1,i) <- runOSGL' n case n1 of (SceneNode num (MatrixTransform m)) -> do let m' = action m replaceNode (SceneNode num (MatrixTransform m')) sn -> do let n'' = addNode (SceneNode (0,"") (MatrixTransform identityMatrix)) [((),i)] self n'' -- | Transform the node of a scene graph with the supplied matrix transform transformSG' :: SceneGraph -> Node -> (MatrixD ->MatrixD ) -> SceneGraph transformSG' sg nde mf = case llab sg nde of (SceneNode _ (MatrixTransform m)) -> replaceNode'' sg (SceneNode (nde,show nde) (MatrixTransform (mf m))) _ -> error "FIXME: Not a transform node" translateSG' :: SceneGraph -> Node -> VectorD -> SceneGraph translateSG' sg nde v = transformSG' sg nde (translateM v) translatePostSG' :: SceneGraph -> Node -> VectorD -> SceneGraph translatePostSG' sg nde v = transformSG' sg nde (translatePostM v) rotatePostSG' :: SceneGraph -> Node -> VectorD -> GLdouble -> SceneGraph rotatePostSG' sg nde v theta = transformSG' sg nde (rotatePostM theta v) -- | Add colour to a node colour :: Monad m => OSGT m SceneNode -> Colour -> OSGT m SceneNode colour n c = colourSG n (\ p -> colour2Phong c) ( (flip colour) c) -- | Label a node label :: Monad m => OSGT m SceneNode -> String -> OSGT m SceneNode label anode lbl = do (SceneNode (nde,_) dte,_) <- runOSGL' anode replaceNode (SceneNode (nde,lbl) dte) -- | Add texture texture :: Monad m => OSGT m SceneNode -> String -> OSGT m SceneNode texture n texName = do (n1,i) <- runOSGL' n addNode (SceneNode (0,"") (Texture texName )) [((),i)] -- | Add Text text :: Monad m => String -> OSGT m SceneNode text str = addBasicNode (Text str) infixr 5 <+> infixl 9 <*> infixl 9 -- | Join two graphs together (<+>) :: Monad m => OSGT m SceneNode -> OSGT m SceneNode -> OSGT m SceneNode (<+>) a b = do s <- ST.get (a', s',i) <- runOSGL s a (b', s'',j) <- runOSGL s' b ST.put s'' addNode (SceneNode (0,"") Group ) [((),i),((),j)] -- | Translate a node (<*>) :: Monad m => OSGT m SceneNode -> VectorD -> OSGT m SceneNode (<*>) = translate -- | Scale a node ():: Monad m => OSGT m SceneNode -> VectorD -> OSGT m SceneNode () = scale doNothing _ = return () -- | Add an handler node handler :: Monad m => OSGT m SceneNode -> ClickHandler -> OSGT m SceneNode handler n f = do (n1,i) <- runOSGL' n addNode (SceneNode (0,"") (Handler (Just (f,doNothing)) Nothing)) [((),i)] handler2 :: Monad m => OSGT m SceneNode -> (ClickHandler,DragHandler) -> OSGT m SceneNode handler2 n (f,g) = do (n1,i) <- runOSGL' n addNode (SceneNode (0,"") (Handler (Just (f,doNothing)) (Just (g,doNothing)))) [((),i)] -- | Create a DragHandler dragHandler :: DragHandler dragHandler (sg,nde) vec = do let tnde = head' "dragHandler 1" $ pre sg nde sg' = translateSG' sg tnde vec SceneNode _ (MatrixTransform m) = llab sg' tnde posx = (head' "dragHandler 2" (toLists m))!!3 return $ (if abs posx < 1 then sg' else sg,posx) -- | Create a ClickHandler switchHandler :: ClickHandler switchHandler (sg,nde) ev = do let sn = head' "switchHandler" $ suc sg nde sn' = llab sg sn let sg' = switchNode sn' (if ev == Down then 1 else 0) sg return sg' switchNode' nde n = \gr -> let (SceneNode _ (Switch _)) = llab gr nde in replaceNode'' gr (SceneNode (nde,show nde) (Switch n)) switchNode :: SceneNode -> Int -> SceneGraph -> SceneGraph switchNode (SceneNode nde (Switch _)) n = \gr -> replaceNode'' gr newNode where newNode = SceneNode nde (Switch n) -- | Create a switch node switch :: Monad m => OSGT m SceneNode -> OSGT m SceneNode -> OSGT m SceneNode switch a b = switch' 0 a b switch':: Monad m => Int -> OSGT m SceneNode -> OSGT m SceneNode -> OSGT m SceneNode switch' nde a b = do s <- ST.get (a', s',i) <- runOSGL s a (b', s'',j) <- runOSGL s' b ST.put s'' n <- addNode (SceneNode (nde,show nde) (Switch 0)) [((),i),((),j)] return n -- | Wrapper for running the OSG monad to return a scene graph and root node. osg :: Monad m => OSGT m SceneNode -> m Scene osg f = do (n,state,_) <- runOSG emptyState f return (gr state,idd n) -- | Create and run a OSG monad to return a scene graph and root node. runOSG :: Monad m => OSGState -> OSGT m SceneNode -> m (SceneNode,OSGState,Node) runOSG state f = do (ret, state') <- ST.runStateT f state return (ret, state',root state') runOSGShow f = do let (ret, state,i) = runOSG emptyState f putStrLn $ show $ (ret,state,i) idd (SceneNode (i,_) _ ) = i -- | Get a strip mesh strip :: Monad m => OSGT m SceneNode strip = do let n = SceneNode (0,"") (Geode $ Mesh1 [(TriangleStrip,0,3)] [ vector3 (-2) 0 (-2), vector3 (2) 0 (-2), vector3 0 0 0 ] [ vector3 0 (-1) 0, vector3 0 (-1) 0, vector3 0 (-1) 0 ] ) addNode n [] -- | Make a group node from list of nodes makeGroup :: Monad m => [SceneNode] -> OSGT m SceneNode makeGroup (n:[]) = addNode n [] makeGroup (n:ns) = let n' = makeGroup ns in (addNode n []) <+> n' emptyScene :: Scene emptyScene = (empty,0) r :: GLdouble r = 5.0 getHitAction :: Scene -> (GLuint -> IO ()) getHitAction _ = (\n -> return ()) -- | Work up the tree from indicated no to find the first handler scene node. findHandler :: SceneGraph -> GLuint -> Maybe SceneNode findHandler gr num = let start = fromEnum num findUp num = case llab gr num of SceneNode (id,_) (Handler f _) -> [llab gr id] _ -> concatMap findUp (pre gr num) in case (findUp start) of [] -> Nothing (a:_) -> Just a -- | Work down the tree from indicated no to find the first handler scene node. findHandlerDown :: SceneGraph -> Int -> Int findHandlerDown gr num = let findDown num = case llab gr num of SceneNode (id,_) (Handler f _) -> [id] _ -> concatMap findDown (suc gr num) in case (findDown num) of [] -> error "findHandlerDown failed" (a:_) -> a findTextDown :: SceneGraph -> Int -> Int findTextDown gr num = let findDown num = case llab gr num of SceneNode (id,_) (Text _ ) -> [id] _ -> concatMap findDown (suc gr num) in case (findDown num) of [] -> error "findHandlerDown failed" (a:_) -> a {-- -- Buttons are always switch nodes but selected geometry will not be so we need to search -- up to find the owning widget. -- FIXME use switchNode? --} -- | Handle some event handleClickEvent :: Scene -> GLuint -> KeyState -> IO (Scene, Maybe Scene,Maybe ( SceneGraph -> SceneGraph )) handleClickEvent (gr,start) n ks = do -- putStrLn $ "handle event" ++ show ks case (findHandler gr n) of Just (SceneNode (id,_) (Handler (Just (fn,snk)) _ )) -> do sg <- fn (gr,id) ks case ks of Down -> snk () _ -> return () return ((sg,start),Just (sg,id),Nothing) _ -> return ((gr,start),Nothing,Nothing) llab gr n = case (lab gr n) of Nothing -> error $ "Shouldnot happen gr=" ++ (show gr) ++ "n = " ++(show n) Just n' -> n' emptyOSG :: SceneGraph emptyOSG = empty mapColour :: Colour -> Color4 GLfloat mapColour Red = Color4 1 0 0 1 mapColour Green = Color4 0 1 0 1 mapColour Blue = Color4 0 0 1 1 mapColour Grey = Color4 0.4 0.4 0.4 1 mapColour LightBlue = Color4 0.3 0.3 1.0 1 mapColour Black = Color4 0 0 0 1 mapColour White = Color4 1 1 1 1 mapColour Yellow = Color4 1 1 0 1 mapColour JustWhite = Color4 0.9 0.9 0.9 1 findCamera :: Scene -> Int -> Node findCamera (gr, nde) i = head' "findCamera" $ filter (\x -> case (llab gr x) of SceneNode _ Camera -> True _ -> False) (nodes gr) findCameraPath :: Scene -> Int -> Path findCameraPath (gr, nde) i = let nde2 = findCamera (gr,nde) i in esp nde nde2 gr -- | Return the matrix got by traversing down the Node getTransformTo :: Scene -> Node -> MatrixD getTransformTo (gr,start) nde = foldr trans identityMatrix $ esp start nde gr where trans n mat1 = case llab gr n of SceneNode _ (MatrixTransform mat2) -> mat1 `mul` mat2 _ -> mat1 --asVec3 :: Vector GLdouble -> VectorD --asVec3 v = let [x,y,z] = toList v in Vector3 (realToFrac x) (realToFrac y) (realToFrac z) getByLabel :: SceneGraph -> String -> Node getByLabel gr lbl = head' "getByLabel" $ filter (\n -> let (SceneNode (_,lbl') _) = llab gr n in lbl == lbl') (nodes gr) -- | A box. Used for calculating bounds type Box a = (Vector a, Vector a) -- | Bounds suitable for starting off with smallBox :: Box GLdouble smallBox = (fromList [ (-0.1), (-0.1), (-0.1)], fromList [0.1,0.1,0.1]) -- | Create union of two boxes union :: (Ord a,Storable a) => Box a -> Box a -> Box a union (v1,v2) (w1,w2) = let v1' = toList v1 v2' = toList v2 w1' = toList w1 w2' = toList w2 in (fromList $ map (uncurry min) $ zip v1' w1', fromList $ map (uncurry max) $ zip v2' w2') -- | Determine bounds of the scene bounds :: Scene -> Box GLdouble bounds (gr,nde) = let sn = llab gr nde in boundsSceneNode gr sn -- | Determine bounds of a @SceneNode@ boundsSceneNode gr (SceneNode (nde,_) (MatrixTransform mt)) = let (v1,v2) = boundsOfChildren gr nde in (mt `mulV` v1, mt `mulV` v2) boundsSceneNode gr (SceneNode (nde,_) (Switch i)) = let nde' = (suc gr nde)!!i in bounds (gr,nde') boundsSceneNode gr (SceneNode _ (Geode (GLObj o))) = smallBox boundsSceneNode gr (SceneNode (nde,_) _) = boundsOfChildren gr nde boundsOfChildren gr nde = maybe smallBox id $ foldr f Nothing (suc gr nde) where f nde Nothing = Just $ bounds (gr,nde) f nde (Just b) = Just $ b `union` bounds (gr,nde)