chickadee » hyperscene

Hyperscene

Hyperscene is a scene library – made for placing objects in a shared world for the purpose of rendering – for CHICKEN Scheme. Hyperscene features a scene graph, cameras with a variety of movement types, frustum culling based on an configurable spatial partitioning system, and a lighting extension. Hyperscene is target agnostic: it is not bound to any particular rendering target and should work equally well for curses, OpenGL, and anything in between.

Hyperscene is a set of bindings to the Hyperscene C library. It’s fairly rough around the edges for general use in Scheme. It should generally be wrapped in order to make it more palatable. Hypergiant is one such library that wraps Hyperscene for use with OpenGL.

Some rendering options are defined at compile time. See render-camera for details.

Requirements

Documentation

Hyperscene’s scenes rely on a number of elements to be in place before a scene can be rendered. First is the scene itself. A scene could be thought of as the world or coordinate system that serves as the base for all the rendering operations. Second is a node. A node is the “physical” thing that can being rendered. Nodes can be added to scenes, or can be added to each other if you want a node to be defined in terms of its relation to another (hence the scene “graph”). Third is a camera. Cameras have a position and orientation in a scene, as well as a projection. Cameras can be rendered, which renders the part of the scene that they are pointing at. The fourth element that must be present is a pipeline. Pipelines are the collection of functions that explain how to render a node. If a node is to be rendered, it must have a pipeline associated with it.

These four elements are all represented as c-pointers. Passing the wrong pointer to the wrong function will result in bad things happening. Sorry.

The basic use of Hyperscene is as follows: First you create pipelines and a scene. Then you add nodes to that scene (or to nodes that are already in the scene). These nodes are assigned a pipeline that knows how it can draw them. Then you create a camera associated with that scene, that has a particular position, orientation, and projection. Cameras are then called upon to render the scene.

Scenes

Scenes can be either active or inactive. The only difference is that active scenes are updated with a call to update-scenes.

make-sceneprocedure

Create a new scene. The scene’s space is partitioned with the partition interface given by partition-interface. New scenes are automatically activated.

delete-scene SCENEprocedure

Delete the given scene.

activate-scene SCENEprocedure

Activate the given scene. If the scene is already active, this has no effect.

deactivate-scene SCENEprocedure

Deactivate the given scene.

update-scenesprocedure

Update all active scenes. This must be called every frame in order to make sure all nodes are positioned correctly.

Nodes

Nodes are the elements that are rendered in Hyperscene. They have five primary properties:

The following functions are used create, delete, and work with nodes:

add-node PARENT DATA PIPELINE DELETEprocedure

Create a new node with the given parent. PARENT can either be a scene or another node. DATA is a user supplied pointer to some data which is then passed to the PIPELINE functions as well as to DELETE when the node is deleted.

delete-node NODEprocedure
unsafe-delete-node NODEprocedure

Delete the given node, removing it from the scene and calling DELETE on its DATA. unsafe-delete-node should only be called if the DELETE function is a pure C function that not call back into Scheme.

node-scene NODEprocedure

Return the scene that the node belongs to.

set-node-bounding-sphere! NODE RADIUSprocedure

Set the radius of the node’s bounding sphere. This is important to set so that Hyperscene knows when the node is inside a camera’s bounding volume or not. When a node is created, the bounding sphere radius is initially set to 1.

node-bounding-sphere NODEprocedure

Return the #f32(x y z radius) bounding sphere of the node. The bounding sphere is positioned in world coordinates. Modifying the returned f32vector will have no effect.

set-node-position! NODE POINTprocedure

Set the position of the node, relative to its parent, to be the #f32(x y z) POINT.

move-node! NODE VECTORprocedure

Move the node by the #f32(x y z) VECTOR.

node-position NODEprocedure

Return the #f32(x y z) position of the node relative to its parent. Modifying this value will not change the node’s position.

node-rotation NODEprocedure

Return a pointer to the node’s quaternion (x y z w) that describes the rotation of the node relative to its parent. Modifying this quaternion (e.g. with gl-math’s imperative quaternion functions) will rotate the node. Make sure to call node-needs-update! after modifying the returned quaternion.

node-needs-update! NODEprocedure

Nodes need to be informed when they have been modified in such a way that they need to be updated. Most node modification functions (set-node-position!, move-node!, set-node-bounding-sphere!) call this automatically, but Hyperscene cannot tell when a node’s rotation quaternion has been modified. Make sure to call node-needs-update! after modifying node-rotation’s return value.

node-transform NODEprocedure

Return a pointer to the 4x4 transform matrix that describes the position and orientation of the node in world space. Consecutive elements of the matrix represent columns. Any modifications to the transform matrix will be lost when the scene is updated.

node-data NODEprocedure

Return a pointer to the node’s user supplied data.

Memory management
set-node-pool-size! SIZEprocedure

Hyperscene uses memory pools to store its data relating to nodes, which makes creation and deletion of nodes and scenes quick. For best performance, set the node pool size to be as large as the greatest number of nodes that will be needed for a scene. When a scene is created with make-scene its node pool is set to this size. Defaults to 4096.

Pipelines

Pipelines are structures consisting of three functions: a pre-render function, a render function, and a post-render function. When a scene (camera) is rendered, the visible nodes are sorted by their pipelines before they are drawn. Then, for every group of pipelines, the pre-render function is called with the first node as an argument. Every node is then passed to the render function. Finally, the post-render function is called to clean up. The sorting is done – and the pre/post-render functions are only called once – in order to minimize the amount of state changes that need to occur during rendering.

The exception to this is when a pipeline represents an element that could be partially transparent. “Alpha” pipelines get drawn after all the other ones, and the nodes that are associate with alpha pipelines are always drawn in order of decreasing distance from the camera. This ensures that the transparent parts can be rendered correctly.

When targeting OpenGL, one pipeline per shader program is generally desirable. The pre-render function should therefore call gl:use-program, while the render function should not. The post-render function can reset any state (e.g. (gl:use-program 0), etc).

add-pipeline PRE-RENDER RENDER POST-RENDER #!optional ALPHAprocedure

Create a new pipeline with the given callbacks (or pointers to pure C functions). ALPHA? indicates whether or not the pipeline can render any transparent elements (defaults to #f).

delete-pipeline PIPELINEprocedure

Delete the given pipeline.

Cameras

Cameras, aside from having an orientation and position within a given scene, have two main properties. Their type is the sort of projection that the camera uses: either orthographic or perspective. The style of the camera indicates the way in which the camera can be moved (see Movement and rotation for details of each function):

The following functions are used create, delete, and work with cameras:

(make-camera TYPE STYLE SCENE [near: NEAR] [far: FAR] [angle: ANGLE] [width: WIDTH] [height: HEIGHT] [viewport-width-ratio: VIEWPORT-WIDTH-RATIO] [viewport-height-ratio: VIEWPORT-HEIGHT-RATIO] [static-viewport?: STATIC-VIEWPORT?])procedure

Create a new camera associated with the given scene. TYPE must be one of #:ortho or #:perspective for an orthographic or a perspective camera, respectively. STYLE must be one of #:position, #:look-at, #:orbit, or #:first-person. New cameras are automatically activated. NEAR is the near plane of the camera, defaulting to 1. FAR is the far plane of the camera, defaulting to 10000. ANGLE is the view-angle, in degrees, for perspective cameras, defaulting to 70. WIDTH and HEIGHT should be initialized to the size of camera’s viewport. VIEWPORT-WIDTH-RATIO and VIEWPORT-HEIGHT-RATIO scale the camera’s viewport (its view frustum’s near plane) in the width and height direction. The effects of the scaling persist after resize-cameras is called. If STATIC-VIEWPORT? is #t, the camera’s viewport dimensions will be fixed such that they won’t be changed by resize-cameras, although VIEWPORT-WIDTH-RATIO and VIEWPORT-HEIGHT-RATIO still effect the final viewport size.

delete-camera CAMERAprocedure

Delete the given camera.

render-camera CAMERAprocedure

Render the given camera. When cameras are rendered, all of the visible nodes are sorted: first into groups of nodes that have an alpha pipline or that don’t.

Alpha nodes are sorted by decreasing distance from the camera and rendered last. There are two sorting schemes that may be employed. The first, and default, scheme is useful when working with one-dimensional alpha objects. It sorts the distance of nodes based only on their origin, not taking into account their bounding sphere. The second scheme, enabled by defining #:volumetric-alpha, is useful when working with three-dimensional alpha objects, and sorts distance while taking the bounding sphere into account.

Non-alpha nodes are sorted by pipeline. Each pipeline is then sorted again by increasing distance from the camera before they are rendered. By doing so, the things that are closest to the camera are drawn first (“reverse painter” sorting) which can help graphics hardware determine when later bits of the scene are hidden, thus saving some rendering time. Not all applications will benefit from this extra step, though, and it can be disabled by defining #:no-reverse-painter at compilation time.

update-camera CAMERAprocedure

Update the given camera. This updates the view matrix of the camera to reflect any changes that may have occurred. This should always be done before rendering.

activate-camera CAMERAprocedure

Add the camera to the list of active cameras (or push it to the back of the list, thus setting it to be rendered last). New cameras are automatically activated.

deactivate-camera CAMERAprocedure

Remove the camera from the list of active cameras.

render-camerasprocedure

Render all the active cameras.

update-camerasprocedure

Update all the active cameras.

resize-cameras WIDTH HEIGHTprocedure

Modify the projection matrix of all cameras, based on the viewport dimensions WIDTH and HEIGHT. Should be called whenever the window is resized. The viewport dimensions of a camera are scaled by any values passed to set-camera-viewport-ratio! or the viewport-*-ratio keywords of make-camera. If static-viewport? was set to #t when a camera was created, this function has no effect on it.

set-camera-clip-planes! CAMERA NEAR FARprocedure

Set the near and far clip planes of the camera. Nodes closer to or further away from these plans will not be visible.

set-camera-view-angle! CAMERA ANGLEprocedure

Set the viewing angle of the perspective camera to angle degrees. This doesn’t have any effect on orthographic cameras.

set-camera-viewport-ratio! CAMERA WIDTH HEIGHTprocedure

Scale CAMERA’s viewport (its view frustum’s near plane) in the width and height direction by WIDTH and HEIGHT. The effects of the scaling persist after resize-cameras is called. This is equivalent to setting the viewport-*-ratio keywords of make-camera.

set-camera-viewport-dimensions! CAMERA WIDTH HEIGHTprocedure

Set CAMERA’s viewport (its view frustum’s near plane) dimensions to WIDTH and HEIGHT, and fixes these dimensions such that they will not be changed when resize-cameras is called.

set-camera-viewport-screen-position! CAMERA LEFT RIGHT BOTTOM TOPprocedure

Set the area of the screen that CAMERA renders to, defined by the rectangle of LEFT, RIGHT, BOTTOM, TOP. These default to the full screen, which is represented by values of -1, 1, -1, 1, respectively.

set-camera-viewport-offset! CAMERA X Yprocedure

Move CAMERA’s viewport (its view frustum’s near plane) by (X, Y) expressed as a fraction of the viewport’s width and height. This is generally only useful for a perspective projection, when lines should converge not to the middle of the screen, but to another point. Setting x to 0.5, for example, moves the focal centre to the right edge of the viewport.

Movement and rotation
move-camera! CAMERA VECTORprocedure

Move the position of the camera by the vector #f32(x y z). Cannot be called with an orbit camera.

set-camera-position! CAMERA POINTprocedure

Set the position of the camera to the #f32(x y z) point . Cannot be called with an orbit camera.

camera-position CAMERAprocedure

Return the #f32(x y z) position of the camera. Modifying this vector will not affect the camera.

camera-rotation CAMERAprocedure

Return a pointer to the node’s quaternion (x y z w) that describes the rotation of the camera. Modifying this quaternion (e.g. with gl-math’s imperative quaternion functions) will rotate position cameras. The returned quaternion must not be modified for any other camera styles.

camera-look-at! CAMERA POINTprocedure

Set the #f32(x y z) point the look-at or orbit cameras are looking at.

set-camera-up! CAMERA UPprocedure

Set the camera’s #f32(x y z) up-vector. Cannot be called with a non-look-at camera.

yaw-camera! CAMERA ANGLEprocedure

Add ANGLE radians to the orbit or first-person camera’s yaw.

set-camera-yaw! CAMERA ANGLEprocedure

Set the yaw of the orbit or first-person camera to ANGLE radians.

pitch-camera! CAMERA ANGLEprocedure

Add ANGLE radians to the orbit or first-person camera’s pitch.

set-camera-pitch! CAMERA ANGLEprocedure

Set the pitch of the orbit or first-person camera to ANGLE radians.

roll-camera! CAMERA ANGLEprocedure

Add ANGLE radians to the orbit or first-person camera’s roll.

set-camera-roll! CAMERA ANGLEprocedure

Set the roll of the orbit or first-person camera to ANGLE radians.

zoom-camera CAMERA DISTANCEprocedure

Add DISTANCE to the orbit camera’s zoom.

set-camera-zoom CAMERA DISTANCEprocedure

Set the zoom of the orbit camera to DISTANCE.

move-camera-forward! CAMERA DISTANCEprocedure

Move the first-person camera forward by DISTANCE. Only the camera’s yaw is taken into account for the movement.

move-camera-up! CAMERA DISTANCEprocedure

Move the first-person camera up by DISTANCE.

strafe-camera! CAMERA DISTANCEprocedure

Move the first-person camera to the right by DISTANCE. Only the camera’s yaw is taken into account for the movement.

Camera matrix and position access
camera-projection CAMERAprocedure

Returns a pointer to the projection matrix of the camera.

camera-view CAMERAprocedure

Returns a pointer to the view matrix of the camera.

camera-view-projection CAMERAprocedure

Returns a pointer to the projection * view matrix of the camera.

Currently rendering camera

While rendering, it can be desirable to have pointers to various matrices relating to the camera and node being rendered (e.g. to be used as uniform values). These pointers always point to the relevant value of the camera currently being rendered.

current-camera-positionprocedure

Returns a pointer to the (x y z) position of the camera currently being rendered.

current-camera-viewprocedure

Returns a pointer to the view matrix of the camera currently being rendered.

current-camera-projectionprocedure

Returns a pointer to the projection matrix of the camera currently being rendered.

current-camera-view-projectionprocedure

Returns a pointer to the projection * view matrix of the camera currently being rendered.

current-camera-model-view-projectionprocedure

Returns a pointer to the projection * view * model matrix of the node currently being rendered.

current-inverse-transpose-modelprocedure

Returns a pointer to the inverse transpose model matrix of the node currently being rendered. This matrix is useful for lighting. If it is not wanted, the calculation of this value can be omitted by defining the feature #:no-inverse-transpose at compile time.

Spatial Partitioning

Hyperscene only renders nodes that are within the bounds of a camera (i.e. it performs view frustum culling). In order for it to efficiently sort through the nodes, a spatial partitioning system is used. Different spatial partitioning systems can be used on a per-scene basis.

If you wish to write a new partition interface, see the Hyperscene documentation.

partition-interfaceprocedure
set-partition-interface! INTERFACEprocedure

Returns/sets the partition interface that scenes are initialized with. Defaults to aabb-tree-interface.

aabb-tree-interfaceprocedure

aabb-tree-interface is a hybrid AABB ternary tree/nonatree/isoceptree inspired heavily by Dynamic Spatial Partitioning for Real-Time Visibility Determination. When trees split, they try to split only along those axis where the nodes are most well dispersed. For example, if you have a 2D game, chances are nodes will be arranged along the X and Y axes, with little separation on the Z axis. In this situation, when enough nodes are added to a given AABB tree, it will only split along those two axes. In doing so, it avoids extraneous tree creation. This can be taken advantage of most in 2D situations by not using too much (Z) distance between layers.

set-aabb-tree-pool-size! SIZEprocedure

Set the memory pool size of the aabb-tree-interface. This pool sets the number of trees in the partition interface’s pool, which is initialized for each scene when make-scene is called. Defaults to 4096.

Extensions

Hyperscene features an extension system, so that the rendering of a scene can be augmented in new and exciting ways.

Extensions can add special nodes to scenes. If a node is created that is given a pointer to an extension in place of a pipeline, that node will not be rendered but will instead be handled by its extension during rendering and updating.

activate-extension SCENE EXTENSIONprocedure

Before an extension can be used in a given scene, it must be activated.

Lights

Hyperscene supplies an extension that provides a generic lighting system:

lightingprocedure

Before lights can be used in a scene, lighting must be activated with activate-extension.

max-lightsprocedure

Return the maximum number of lights that may be visible at once. Defaults to 8.

set-max-lights! Nprocedure

Set the maximum lights that may be visible at once. Must not be called after the first scene with lighting is initialized.

set-ambient-light! SCENE COLORprocedure

Scenes that use the lighting extension have an #f32(r g b) ambient light associated with them, set by this function.

(add-light PARENT COLOR INTENSITY [direction: direction] [spot-angle: SPOT-ANGLE])procedure

Adds a new light to the given PARENT node (or scene) with #f32(r g b) COLOR. INTENSITY is the floating point value associated with the brightness of the light. DIRECTION is an #f32(x y z) vector that indicates the direction that the light is pointing, defaulting to #f32(0 0 0). SPOT-ANGLE indicates the angle in radians that the light is spread over (defaulting to 0, representing a non-spotlight source). A node is returned that can be moved, rotated, and sized like any other node.

set-light-color! LIGHT COLORprocedure

Sets the #f32(r g b) color of the light.

light-color LIGHTprocedure

Returns the #f32(r g b) color of the light.

set-light-intensity! LIGHT INTENSITYprocedure

Sets the intensity of the light.

light-intensity LIGHTprocedure

Returns the intensity of the light.

set-light-direction! LIGHT DIRECTIONprocedure

Sets the #f32(x y z) direction of the light.

light-direction LIGHTprocedure

Returns the #f32(x y z) direction of the light.

set-light-spot-angle! LIGHT ANGLEprocedure

Sets the angle, in radians, over which the light is spread.

light-spot-angle LIGHTprocedure

Returns the angle over which the light is spread.

n-current-lightsprocedure

Returns a pointer to the number of visible lights in the scene currently being rendered.

current-ambient-lightprocedure

Returns a pointer to the (r g b) color of ambient light in the scene currently being rendered.

current-light-positionsprocedure

Returns a pointer to the array of packed (x y z) positions of the visible lights in the scene currently being rendered.

current-light-colorsprocedure

Returns a pointer to the array of packed (r g b) colors of the visible lights in the scene currently being rendered.

current-light-intensitiesprocedure

Returns a pointer to the array of intensities of the visible lights in the scene currently being rendered.

current-light-directionsprocedure

Returns a pointer to the array of packed (x y z spotAngle) directions and angles of the visible lights in the scene currently being rendered.

make-material R G B SHININESSprocedure

Make a simple material definition consisting of an RGB color (specular color, perhaps) and shininess value. Returns a four element f32vector, located in non-garbage-collected memory. Intended for use as a uniform value.

set-material-specular-color! MATERIAL R G Bprocedure

Set the RGB values of the given material.

set-material-shininess! MATERIAL SHININESSprocedure

Set the shininess value of the given material.

set-light-pool-size! SIZEprocedure

Every scene is given a pool from which to allocate lights, the size of which can be modified by calling this function before initialization (defaults to 1024).

Writing your own extensions

The Hyperscene C library is extensible in C. See its documentation for details.

Examples

See the examples directory to see Hyperscene in action.

Version history

Version 0.3.0

20 January 2014

Version 0.2.0

20 December 2014

Version 0.1.0

Source repository

Source available on GitHub.

Bug reports and patches welcome! Bugs can be reported via GitHub or to alex.n.charlton at gmail.

Author

Alex Charlton

License

BSD

Contents »