Next: , Previous: Statemachine, Up: Top



6 About Primitives

Primitives are objects that you can render. There isn't really much else in a fluxus scene, except lights, a camera and lots of primitives. See Primitives, to see all the types of primitives you can render.

6.1 Retained Mode

The normal way to create a primitive is to call it's build function and then use it's returned ID (either directly, or by grabbing) to modify it later.

     (define myobj (build-cube))
     (grab myobj)
     (colour (vector 1 0 0))
     (ungrab) ; always remember to ungrab...

6.2 Primitive Data [aka. pdata]

Each primitive is made up of arrays of data that describe it's shape and surface properties. The data is always stored per-vertex, and is created when you call the build function. You do not have to know anything about this data to use primitives, but it means you can modify or deform them in a more detailed way than using functions we've already looked at, and some primitives are only useful if pdata is used to control them.

So, for example (build-sphere) creates a polygonal object with a spherical distribution of vertex point data, surface normals at every vertex and texture coordinates, so you can wrap a texture around the primitive. This data (primitive data, or pdata for short) can be read and written to by your scripts, using the pdata-set and pdata-get commands (which operate on the currently grabbed object). See PrimitiveData, function reference for more detail.

     (pdata-set name vertnumber vector)

Sets the data on the currently (grab)ed object to the input vector

     (pdata-get name vertnumber)

Returns the vector from the pdata on the currently (grab)ed object

     (pdata-size)

Returns the size of the pdata on the currently (grab)ed object (the number of verts)

The name describes the data we want to access, for instance "p" contains the vertex positions:

So, some examples to make sense of this:

     (pdata-set "p" 0 (vector 0 0 0))

Sets the first point in the primitive to the origin (not all that useful)

     (pdata-set "p" 0 (vadd (pdata-get "p" 0) (vector 1 0 0)))

The same, but sets it to the original position + 1 in the x offsetting the position is more useful as it constitutes a deformation of the original point. (See Deforming, for more info on deformations)

The pdata availible depends on the type of the primitive.

Polygonal based primitives (Cube, Sphere, Cylinder, Plane, Text)

     Positions:      "p" vector
     Normals:        "n" vector
     Texture coords: "t" vector
     Vertex colours: "c" colour

NURBS based primitives (NURBSSphere, NURBSPlane)

     Positions:      "p" vector
     Normals:        "n" vector
     Texture coords: "t" vector

6.2.1 Particle primitive pdata

     Positions:      "p" vector
     Colours:        "c" colour
     Sizes:          "s" vector

6.2.2 Line primitive pdata

     Positions:      "p" vector
     Width:          "w" float

6.2.3 Blobby primitive pdata

Each element of pdata corresponds to an influence.

     Positions:      "p" vector
     Strength:       "s" float
     Colour:         "c" vector

6.2.4 Pixel primitive pdata

     Colour:      "c" vector
     Alpha:       "a" float

6.3 Topology and pdata

The order and meaning of the vertex pdata depends on the exact primitive, and in the case of polygonal objects, the topology of the polygons.

So a polygon primitive built from triangle lists will have the following ordering of it's pdata:

(This is a really bad way of describing something which is easier done with a picture, see your OpenGL manual, or search the net for "OpenGL Primitives" :)

     pdata index 0 : face 0 vert 0
     pdata index 1 : face 0 vert 1
     pdata index 2 : face 0 vert 2
     pdata index 3 : face 1 vert 0
     pdata index 4 : face 1 vert 1
     pdata index 5 : face 1 vert 2
     
     Quad lists:
     
     pdata index 0 : face 0 vert 0
     pdata index 1 : face 0 vert 1
     pdata index 2 : face 0 vert 2
     pdata index 3 : face 0 vert 3
     pdata index 4 : face 1 vert 0
     pdata index 5 : face 1 vert 1
     
     Triangle strip:
     
     pdata index 0 : face 0 vert 0
     pdata index 1 : face 0 vert 1 & face 1 vert 0
     pdata index 2 : face 0 vert 2 & face 1 vert 1 & face 2 vert 0
     pdata index 3 : face 1 vert 2 & face 2 vert 1 & face 3 vert 0
     pdata index 4 : face 2 vert 2 & face 3 vert 1 & face 4 vert 0
     pdata index 5 : face 3 vert 2 & face 4 vert 1 & face 5 vert 0
     
     Triangle fan:
     
     pdata index 0 : vert 0 for all faces
     pdata index 1 : face 0 vert 1
     pdata index 2 : face 0 vert 2 & face 1 vert 1
     pdata index 3 : face 1 vert 2 & face 2 vert 1
     pdata index 4 : face 2 vert 2 & face 3 vert 1
     pdata index 5 : face 3 vert 2 & face 4 vert 1
     
     Polygon (the easy one):
     
     pdata index 0 : vert 0
     pdata index 1 : vert 1
     pdata index 2 : vert 2
     pdata index 3 : vert 3
     pdata index 4 : vert 4
     pdata index 5 : vert 5

This lookup is the same for all the pdata on a particular primitive - vert positions, normals, colours and texture coordinates.

Although this implicit topology means the primitive is optimised to be very quick to render, it costs some memory as points are duplicated. This is a standard tradeoff, the most optimal poly topology are triangle strips, as the duplication gets less, depending on how long your strips get.

The topologies for the various polygon primitives are as follows:

     cube: quad list
     plane: quad list
     text: quad list
     sphere: triangle list
     cylinder: triangle list

6.4 Nurbs topology

Nurbs pdata are much simpler to deal with, as the topology is just a patch grid for both spheres and subdivided planes. For example:

     (build-nurbs-plane 5 5)

Would give you a topology like this:

     0   1   2   3   4
     5   6   7   8   9
     10  11  12  13  14
     15  16  17  18  19
     20  21  22  23  24

6.5 Instancing

Sometimes retained mode primitives can be unwieldy to deal with. For instance, if you are rendering thousands of identical objects, or doing things with recursive graphics, where you are calling the same primitive in lots of different states - keeping track of all the IDs would be annoying to say the least.

This is where instancing is helpful, all you call is:

     (draw-instance myobj)

Will redraw any given object in the current state (immediate mode). An example:

     (define myobj (build-nurbs-sphere 8 10)) ; make a sphere
     
     (define (render-spheres n)
     	(push)
     	(translate (vector n 0 0)) ; move in x
     	(draw-instance myobj)       ; stamp down a copy
     	(pop)
     	(if (< n 0)
     		0
     		(render-spheres (- n 1)))) ; recurse!
     
     (every-frame (render-spheres 10)) ; draw 10 copies

6.6 Built In Immediate Mode Primitives

To make life even easier than having to instance primitives, there are some built in primitives that can be rendered at any time, without being built:

     (draw-cube)
     (draw-sphere)
     (draw-plane)
     (draw-cylinder)

So the it makes the code above simpler:

     (define (render-spheres n)
     	(push)
         (translate (vector n 0 0)) ; move in x
     	(draw-sphere)              ; render a new sphere
     	(pop)
     	(if (< n 0)
     		0
     		(render-spheres (- n 1)))) ; recurse!
     
     (every-frame (render-spheres 10)) ; draw 10 copies

These built in primitives are very restricted in that you can't edit them or change their resolution settings etc, but they are handy to use for quick scripts with simple shapes.