How Visual Rendering is Done

Matthew Leditschke
(Matthew.Leditschke@cselt.it)

Aim

To draw the objects which make up the scene, in back to front order, performing the mimimal amount of drawing.

Proxy nodes

Before describing the scene traversal and rendering process in detail, it is important to say a few words about how the scene tree is stored.

The scene tree, as constructed by the BifsDecoder, consists of a tree of objects which derive from the MediaObject class. The Render() method of these objects is empty.

Attached to every object in this tree is a proxy object, the description of which is provided below. It is these proxy objects which contain the necessary data and methods to perform the necessary traversals of the scene which are required to render the scene.

The different types of proxy objects are defined using a class hierarchy.

The presenter controls the drawing of the scene

The main loop of the presenter (contained in the PresenterCSELT::Main() method) continuously draws the scene at the specified frame rate. Each time through this loop, the presenter does the following steps:
  1. Process events which have been received from the Executive (via the OnUserEvent() method. Processing these events may cause the fields of some nodes to be changed as a result of routes being triggered.

  2. Update the time sensor nodes which are present in the scene. This is done by traversing the tree, calling the UpdateTimeSensor() method of the root proxy object of the scene. When a time sensor is updated, routes may be triggered which affect the fields of other nodes.

    At this point all routes have been triggered which result from user interaction and time based events, and so the rendering of the scene at this point in time can commence.

  3. The presenter now initiates the pre-render traversal of the scene tree, by calling the PreRender() method of the root proxy object. This traversal enables all objects to determine where they will be displayed (cumulation of all of the Transform2D nodes), and what there drawing order is (also given by the Transform2D node).

    During this traversal, nodes which need to be drawn (referred to as drawable nodes) register themselves with the VisualRenderer, using the AddToDrawList() method. Even if a node has not changed since, it must still register, as the VisualRenderer will decide what needs to be redrawn.

  4. The visual renderer is now told to render the nodes which have registered themselves, in depth order. This is discussed in more details below.

  5. A traversal of the scene tree is performed to determine if all of the nodes have finished being rendered. This is done by calling the IsFinished() method on the root proxy object. Once this returns true, the presenter main loop terminates.

  6. At this point all of the rendering for this point in time has finished. The presenter now determines how long it has to wait until the next time the scene needs to be drawn, and sleeps for this period of time.

How the visual renderer coordinates the drawing of the nodes

When instructed by the presenter, the visual renderer sorts the list of drawable nodes based on their draw order, and then draws the nodes in back-to-front order.

Rather than just drawing all nodes in the list, the visual renderer determines the minimum amount of drawing which needs to be performed, based on which nodes have changed, which ones are transparent, and their bounding boxes. This is done as follows:

  1. Start with an empty rectangle which indicates what part of the final output image has changed, and a second empty rectangle which stores what part of the final output image is made up of objects which are partly see-through.

  2. Go through the list of nodes, from front to back, doing the following:
    1. If this node has changed and hence needs to be redrawn, then add its bounding rectangle to the overall changed area bounding rectangle.
    2. If this node is transparent (which means that it does not completely fill its bounding rectangle), then add its bounding rectangle to the overall transparent area bounding rectangle.

  3. Before drawing the nodes, some of the output frame needs to be set to the black background before drawing. This area is indicated by the overall transparent area bounding rectangle.

  4. Draw all of the node, in back-to-front order, only drawing the parts of the objects which lie within the overall changed area bounding rectangle.

    Nodes which have not changed are only drawn if they intersect with the transparent area, or if something behind them has been redrawn.

Using DrawableNode::Draw() and the visual renderer drawing functions

The visual renderer has been designed in a manner which will support the implementation of its functionality on a number of different graphics systems.

When adding new nodes, some of the implementation of the drawing of the node will reside in the Draw() method of the proxy object for the node, and some will be in a node specific method in the visual renderer. The aim is to include as much code as possible in the proxy object, as this is the same for all graphics systems. Only those things which are graphics system specific should be placed into the visual renderer's node specific display methods.

More details

More details on the methods use to traverse the scene tree can be found in the description of the following classes which build up the hierarchy of proxy object classes: