crocodile-0.1: app/src/SceneGraph.hs
-- The graph structure holding the scene
{-# LANGUAGE MagicHash #-}
module SceneGraph (buildSceneGraph, SphereTreeNode(boundingRadius, boundingCentre, object, children), SceneGraph(root, infiniteObjects, finiteBox)) where
import Primitive
import Vector
import BoundingBox
import Data.List
data SphereTreeNode = SphereTreeNode { object :: Maybe Object, children :: [SphereTreeNode], boundingRadius :: !Double, boundingCentre :: !Vector } deriving (Show)
data SceneGraph = SceneGraph { root :: SphereTreeNode, infiniteObjects :: [Object], finiteBox :: AABB } deriving (Show)
-- Find the mean of a collection of objects
calculateMeanPosition' :: [Object] -> Vector -> Vector
calculateMeanPosition' (obj : objects) acc = calculateMeanPosition' objects acc + getCentre obj
calculateMeanPosition' [] acc = acc
calculateMeanPosition :: [Object] -> Vector
calculateMeanPosition objects = setWTo1 (calculateMeanPosition' objects zeroVector </> len')
where
!len' = fromIntegral (length objects)
-- Find the overall bounding radius of a list of objects
calculateBoundingRadius :: [Object] -> Vector -> Double
calculateBoundingRadius objs centre = foldr (Prelude.max . (\obj -> primitiveBoundingRadius (primitive obj) (transform obj) centre)) 0 objs
-- Build up a sphere tree
buildSphereTree :: ([Object] -> [[Object]]) -> [Object] -> SphereTreeNode
buildSphereTree _ (obj : []) = SphereTreeNode (Just obj) [] nodeRadius nodeCentre
where
nodeCentre = calculateMeanPosition [obj]
nodeRadius = calculateBoundingRadius [obj] nodeCentre
buildSphereTree builder (obj:objs)
| length (obj:objs) == 1 = error "Should have been handled by a different pattern"
| null (obj:objs) = error "Should not have zero objects"
| otherwise = SphereTreeNode Nothing nodeChildren nodeRadius nodeCentre
where
nodeCentre = calculateMeanPosition (obj:objs)
nodeRadius = calculateBoundingRadius (obj:objs) nodeCentre
nodeChildren = map (buildSphereTree builder) (builder (obj:objs))
buildSphereTree _ [] = error "Should not hit this pattern for buildSphereTree"
-- Build a scene graph
buildSceneGraph :: [Object] -> ([Object] -> [[Object]]) -> SceneGraph
buildSceneGraph objs buildFunction = SceneGraph (buildSphereTree buildFunction nonInfiniteObjs) infiniteObjs (objectListBoundingBox nonInfiniteObjs)
where
(infiniteObjs, nonInfiniteObjs) = partition (infinite . primitive) objs