SceneGraph

From HaskellWiki

Introduction[edit]

The aim of the Haskell Scene Graph project is to provide a solid basis for the development of 3D applications. Other work which has 3D content has focused on an application or a research topic. Whilst these projects have a scene graph and/or 3D component, it is not easy to extract a useful library. The project's aim is to provide such a library.

Current Status: Initial 0.1.0.2 version is available on Hackage; last update was in June 2010, there is no maintainer. A version of scenegraph that compiles but is no longer able to render is available at https://github.com/homectl/lambdaray/tree/main/scenegraph.

The library draws inspiration from the following Haskell projects:

and from the following non-Haskell work:

Getting Started[edit]

A scene is constructed using a set of combinators. The following creates a green cube

cube 0.5 `colour` Green

To scale the cube we use the 'scale' function. This takes a vector indicating by how much to scale in X,Y and Z directions:

cube 0.5 `colour` Green `scale` v1x 40

v1x is a function that given a number n, returns the vector (n,1,1). This contributes to a concise specification of the scene.So here the cube is scale by 40 in the X direction.

To translate the scene we use a similar function to scale:

cube 0.5 `colour` Green `scale` v1x 40 `translate` vy 2

Finally our scene requires a camera and light to be specified:

cube 0.5 `colour` Green `scale` v1x 40 `translate` vy 2 <+> camera `translate` vy (-10) <+> light `translate` vz 10

In order to run the scene we use runScene. As the above scene has been constructed in a monad, we need to run the monad, to extract the actual Scene data structure.

runScene $ osgt $ cube 0.5 `colour` Green `scale` v1x 40 `translate` vy 2 <+> camera `translate` vy (-10) <+> light `translate` vz 10 

To navigate the scene use the following:

  • W - Move forward
  • S - Move back
  • A - Move left
  • D - Move right
  • Z - Move down
  • X - Move up
  • ESC - Exit

Reactive Scenes[edit]

The module Graphics.SceneGraph.Reactive uses Reactive to enliven a scene. ExampleReactive.hs provides an example of this.This uses a calculator defined independently of the GUI and is made tangible by SceneGraph.

calc = runSceneReactive calculator sceneButtonReactive

calculator is the Scene representing the calculator. The button and display objects are labeled to allow event handlers to be applied during a second pass.

sceneButtonReactive is where the second pass is performed and this wires up the calculator.

Internals[edit]

The Scene Graph is built using FGL. So it remains to define a scene node to get things started:

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

Group - Supports grouping of nodes. Since the Graph will provide the links to children we don't need to have anything more.

Geode - Is where geometries shapes are specified. In theory these nodes should not have children. Geometries can be basic shapes (provided in GLUT.Objects) or meshes of some form.

LOD - Level of detail. Children of this would be representations of the same sub-scene at different level of detail. Choice of which child which depend on current viewing scale. (Currently not implemented).

MatrixTransform - Transform node.

Switch - Provides the ability to choose the sub-tree based on the integer value. One application of this is to provide a representation to be drawn when the object is selected. Some implementations suggest using a bit map across all the children.

Material - Sets the material for the tree starting at this node. Currently just Phong is shown.

Handler - Some form of event handler can be specified at this node. Two types are available - ClickHandler to handle mouse clicks on the object, and DragHandler to handle drag events on the object.

Light - Specifies a light at the origin.

Camera - Specifies a camera at the origin.

Texture- Named texture is applied to the sub-tree.

Text - Text object.

Note that the scene is defined as a graph but when the monad is run a tree is created.

Scene Graph Monad[edit]

Wrapping in Monad enables us to define a simple DSL for building Scene Graphs:

type OSGT m = (StateT OSGState m)

-- Append the two graphs parented at the supplied nodes
(<+>) ::  Monad m => OSGT m SceneNode -> OSGT m SceneNode -> OSGT m SceneNode

-- Translate a node (and child graph)
-- Rotate, scale and colour can be defined in similar ways
translate ::  Monad m => OSGT m SceneNode -> Vector3 Float -> OSGT m SceneNode

-- Example
ferris :: Float -> OSG SceneNode
ferris theta = do 
             let ring = torus 0.5 `colour` White --Blue
                 axial1 = cube 0.5 `scale` v1x 40 `colour` Green
                 axial2 = cube 0.5 `scale` v1z 40 `colour` Green
                 axial = axial1 <+> axial2
             root <- (ring `translate` vy 2 <+> ring `translate` vy (-2) 
                     <+> axial `translate` vy (-2) <+> axial `translate` vy 2 <+> crossbeams) `rotate` (theta , (vector3 0 1 0 ))
             return root

The Monad can be run to extract the actual scene graph and then sent off for viewing.

Limitations[edit]

Currently, system is very fickle about light and camera. These need to be specified and need to be translated immediately.

Further Work[edit]

Build an extended example that uses the library. A puzzle game of some sort.

Texture loading requires program to be run in root directory of package.