Projector scene model

Projector reads a projector script on STDIN to render. The script contains 4 kind of commands:

In other words, the scene is a collection of global states, including a list of named objects present in the scene and all internal geometry and properties of those objects.

The projector scene

The scene consists of a background, a camera, a light source and finite number of objects.

The background is a solid color set using the bg command.

The camera is placed using cam command. Rendering a frame is done by simulating a camera taking a picture placed at the position and angles specified in the cam command, also considering the field-of-view of the "camera lens".

Objects are visible because they reflect light coming from a single light source ("lamp" or "sun") also placed in the scene, using the light command. Normally the light source is placed far away from objects to provide uniform surface lighting. How reflections happen is also affected by global and object-local diffuse and ambient values. The light source also modulates diffusion.

Projector objects

Projector objects are defined using the obj command followed by object commands. All objects are shown (visible) by default, but visibility can be turned on or off from the script any time (even during object definition).

Each object is placed in the scene applying a transformation matrix, which can be modified any time using the pos mat and mat3 commands.

Bulk of the object definition should be about vertices and primitives, constructing the object geometry.

Constructing object geometry from scratch

Projector objects are triangulated surface models. The surface is described as:

The vertex and triangle arrays are stored on a per object basis. There are different methods for creating the surface model.

The simplest method that matches the internal model is to create the vertex array using the verts command, then define triangles using the tri command referencing existing vertices by their index in the array. For example this is how to create a pyramid (without the bottom rectangular case plate):

  verts                  ! 4 base points: 1x1 square between :0 and :3
    -0.5 -0.5 0          ! :0
    +0.5 -0.5 0          ! :1
    +0.5 +0.5 0          ! :2
    -0.5 +0.5 0          ! :3
    0    0    0.5        ! :4  top point

  tri :0 :1 :4           ! draw sides
  tri :0 :4 :3
  tri :2 :4 :1
  tri :2 :3 :4

Vertices in the vertex array are indexed from 0 and referenced as :idx. Order of vertices does matter because of the index based referencing. Order of triangles is arbitrary, but order of the 3 vertices within the triangle determines in which direction the automatically calculated normal vector is pointing (see explanation at nocull).

A shorthand called quad is provided for creating two triangles for a 4-vertex polygon.

Especially when writing a projector object manually, it may be convenient to skip creating the vertex array and simply specify vertices with coordinates in primitives. For example the last tri command of the above example could have been written like this:

  tri     :2     :3     0  0  0.5

Primitives are rendered with either with the last set object color or with their own color if the primitive can specify a color. For example it is possible to create gradient colored triangles and quads, where object color is ignored and color is specified per vertex within the primitive.

There is a textured triangle primitive for textured objects (see below) and a textured quad shorthand too.

A special primitive is line, which is a thin (1 pixel) line between two points in space. Lines are drawn using the lines command.

How the final coloring looks like depends on lighting and reflection, which can be set object-locally using specular diffuse, ambient and shiny. When not set, global defaults are used.

There are shorthand templates for generating the vertices and triangles for common 3d objects:

The template primitives are initially created at unit size, e.g. cube is 1*1*1, but each command takes an optional matrix-expression argument that can scale, displace and rotate the primitive before triangles are generated and appended to the object.

Note: after the template primitive command is executed, the resulting triangles are appended to the object. At the end of the day the object stores an array of low level (non-template) primitives, and it will not be remembered if a specific triangle was created for a cube, cone or tri. So template primitives are really just shorthands to generate triangles.

Texturing objects

Textures are first named and loaded into the global texture list. Then each object may use the texture command to specify which texture should be on the surface of the object.

Textured objects must also call the mapuv mapuv command that creates the uvmap which describes how texture pixels are mapped onto the 3d surface of the object. The mapuv command optionally takes matrix-expression which can be used to transform the uvmap (effectively transforming the texture)

It is possible to declare a global default texture. This texture is rendered on any object that does not have its own texture.

Advanced object features

Objects can contain hot points for event reporting, see below at the event reporting section.

For round surfaces gouraud shading can be enabled using normals.

Constructing objects from existing objects

When a cluster of the same or very similar objects need to be rendered, it is possible to omit creating mutiple copies of the same object and get the rendering engine to work from a single object and render it multiple times differently. This is done by creating a dummy object whose primitive array contains a reference to the other object instead of trinagles. When the rendering engine reaches the reference while rendering object primitives it will recurse to rendering the referenced object.

It is also possible to use this feature to combine or extend objects.

Object references available:

Rendering and timing

The render command exposes a frame using the camera. The frame is then rendered in projector's window or to STDOUT if projector is running in headless mode.

It is possible to modify the scene and run subsequent renders to get different frames. This is normally done for animation, where a steady frame rate is desired. This can be achieved using renderdelay, which will try to keep timing compared to its previous call

In some rare cases such precise timing is not desired and a simpler, more direct sleep can be issued.

Scene modifications

When creating animation the scene is modified between two renders. The scene contains a set of global states, which can be simply overwritten by calling the global command for changing their value. The most typical global changes are moving the cam and light or changing global lighting parameters.

Most objects are dynamic and are never really closed, which means they can be modified between two renders. For example it is possible to transform (move, rotate, scale) existing objects within the scene:

obj NAME mat MEXPR

This will replace the original object transformation matrix with MEXPR for the object called NAME.

Objects can be shown or hidden by name:

obj NAME show
obj NAME hide

Somewhat more advanced, object vertex array and triangle lists are not closed either, so it is possible to append new vertices and triangles, either using low level primitives or template primitives. Since it is not possible to address existing triangles, it is not possible to selectively remove triangles. However, it is possible to remove everything and realempty the object so it can be redefined while the object keeps its name, transformation matrix and non-geometric properties. A lighter eraser is empty, which removes primitives (triangles, colors) but keeps vertices. This is useful is the overall shape of the object will not change but surface coloring will or parts of the surface should be shown or hidden (triangles generated or not generated for them).

Object texture can be overwritten using the texture command or can be removed using texture off.

Event reporting and I/O

Projector can report events (mostly user input) on its STDOUT. This is off by default. It can be turned on by executing projector -e on the command line or by running the events on command.

The script can also print messages using the echo command. This can be especially useful as a timing feedback and synchronization when some emitter is constantly feeding projector and doing renderdelay for steady FPS rendering. An echo can let the emitter read back when projector reached rendering a specific frame.

Projector stops reading STDIN after EOF, but normally keeps running (keeps last frame rendered). This feature is called keepalive and can be turned off (so that projector exits on EOF) using -k on the command line or by running the keepalive off command.

Mouse events are normally reported in screen coordinates, but it is possible to set up an invisible sense plane within the scene. Once set up, each event that reports screen coords will also have 3d space coords reported from the sense plane where the mouse cursor is currently the closest. The sense plane can be set up using the sense or gsense command.

If objects define hot points, coordinate reporting events will also report the NAME of the hot point that is closest to the mouse cursor.

In interactive applications it may be useful to "grab" the mouse: hide the mouse cursor and not let the mouse leave window.

Optimization

When rendering a lot of objects on a large screen with hight frame rate optimization plays a key role. The most obvious ones are not drawing surface triangles that re facing away from the camera (backface culling), not drawing objects that are specific thresholdtoo far away, sometimes masked with fog.

Projector also implements level-of-details mechanisms that allow objects that end up using less pixels rendered cheaper. This is done by replacing a complex object with a reference to a much cheaper, simplified object depending on distance-from-camera. Associated commands are lod and lodcopy

Unchanging stationary objects (e.g. landscape) should be marked static, which allows extra optimization.