SceneGraph
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.