The State Machine
The state machine is the key to understanding how fluxus works, all it really means is that you can call functions which change the current context which has an effect on subsequent functions. This is a very efficient way of describing things, and is built on top of the OpenGl API, which works in a similar way. For example:
(define (draw) (colour (vector 1 0 0)) (draw-cube) (translate (vector 2 0 0)) ; move a bit so we can see the next cube (colour (vector 0 1 0)) (draw-cube)) (every-frame (draw))
Will draw a red cube, then a green cube (in this case, you can think of the (colour) call as changing a pen colour before drawing something). States can also be stacked, for example:
(define (draw) (colour (vector 1 0 0)) (with-state (colour (vector 0 1 0)) (draw-cube)) (translate (vector 2 0 0)) (draw-cube)) (every-frame (draw))
Will draw a green, then a red cube. The (with-state) isolates a state and gives it a lifetime, indicated by the brackets (so changes to the state inside are applied to the build-cube inside, but do not affect the build-cube afterwards). Its useful to use the indentation to help you see what is happening.
The Scene Graph
Both examples so far have used what is known as immediate mode, you have one state stack, the top of which is the current context, and everything is drawn once per frame. Fluxus contains a structure known as a scene graph for storing objects and their render states.
Time for another example:
(clear) ; clear the scenegraph (colour (vector 1 0 0)) (build-cube) ; add a red cube to the scenegraph (translate (vector 2 0 0)) (colour (vector 0 1 0)) (build-cube) ; add a green cube to the scenegraph
This code does not have to be called by
(every-frame), and we use
(build-cube) instead of
(draw-cube). The build functions create a primitive object, copy the current render state and add the information into the scene graph in a container called a scene node.
(build-*) functions (there are a lot of them) return object id's, which are just numbers, which enable you to reference the scene node after it’s been created. You can now specify objects like this:
(define myob (build-cube))
The cube will now be persistent in the scene until destroyed with
with-state returns the result of it’s last expression, so to make new primitives with state you set up, you can do this:
(define my-object (with-state (colour (vector 1 0 0)) (scale (vector 0.5 0.5 0.5)) (build-cube)))
If you want to modify a object's render state after it’s been loaded into the scene graph, you can use
with-primitive to set the current context to that of the object. This allows you to animate objects you have built, for instance:
; build some cubes (colour (vector 1 1 1)) (define obj1 (build-cube)) (define obj2 (with-state (translate (vector 2 0 0)) (build-cube)) (define (animate) (with-primitive obj1 (rotate (vector 0 1 0))) (with-primitive obj2 (rotate (vector 0 0 1)))) (every-frame (animate))
A very important fluxus command is
(parent) which locks objects to one another, so they follow each other around. You can use
(parent) to build up a hierarchy of objects like this:
(define a (build-cube)) (define b (with-state (parent a) (translate (vector 0 2 0)) (build-cube))) (define c (with-state (parent b) (translate (vector 0 2 0)) (build-cube)))
Which creates three cubes, all attached to each other in a chain. Transforms for object a will be passed down to b and c, transforms on b will effect c. Destroying a object will in turn destroy all child objects parented to it. You can change the hierarchy by calling
(parent) inside a
(with-primitive). To remove an object from it's parent you can call:
(with-primitive myprim (detach))
Which will fix the transform so the object remains in the same global position.
Note: By default when you create objects they are parented to the root node of the scene (all primitives exist on the scene graph somewhere). The root node is given the id number of 1. So another way of un-parenting an object is by calling
Note on grabbing and pushing
Fluxus also contains less well mannered commands for achieving the same results as with-primitive and with-state. These were used prior to version 0.14, so you may see mention of them in the documentation, or older scripts.
(push) ... (pop)
is the same as:
(grab myprim) ... (ungrab)
is the same as
(with-primitive myprim ...)
They are less safe than
(with-*) as you can push without popping or vice-versa, and shouldn’t be used generally. There are some cases where they are still required though (and the
(with-*) commands use them internally, so they won’t be going away).