OpenGLContext "Flat" Rendering
This document describes OpenGLContext's new
"flat" rendering process. This process uses client-side (not
GL-side) calculations to produce composed transformation matrices which
are directly loaded before rendering geometry. The rendering
process is considerably less involved than the original set of separate "RenderPass" objects which would traverse the scenegraph for each pass.
Observables and Tree Updates
PyVRML97
allows for watching updates to properties of nodes. OpenGLContext
uses this to watch for all updates to node fields within a scenegraph.
For each path to each node, it records a NodePath object which
can calculate (and cache) the combined transform matrices for the path.
With
this data structure (essentially a list of matrices and Render nodes),
the scenegraph can be rendered with a number of simple iterations,
rather than with a complex traversal mechanism (which traditionally was
a significant factor of OpenGLContext run-time).
The default flat
render pass also includes "colour select" rendering. That is, it
can do a selection rendering pass which can be queried to process
incoming mouse events to find the object under the mouse. This
avoids the use of the legacy select render mode.
Triggering the RenderPass
So let's take a look at how the rendering process is triggered, from
the moment the GUI library sends the "OnPaint" or equivalent event to
the Context through to the calling of an individual RenderPass.
- event handler for the Context object, such as wxOnPaint for the wxPython Context
sub-classes calls self.triggerRedraw(1) to force a redraw of the Context
- Context.triggerRedraw sets
the "alreadyDrawn" flag to false, which tells the context that it needs
to be redrawn at the next available opportunity, if not able to
immediately draw, sets the redrawRequest event.
- at the next available opportunity (which may be within the
triggerRedraw method, depending on the threading status and/or whether
or not we are currently in the middle of rendering), the context's
OnDraw method will be called
- Context.OnDraw
- performs an event cascade (calls the DoEventCascade
customization point while the scenegraph lock is held (by default this
does nothing))
- sets this Context instance as the current context
- acquires the OpenGLContext contextLock
- does the appropriate GUI library set current call
- clears the redrawRequest event
- calls the Context's
renderPasses attribute receiving a flag
specifying whether there was a visible change (flat *always* returns True here)
- if there was a change, swaps buffers
- finally, un-sets the current context
- PassSet.__call__
- instantiates a new
OverallPass object with pointers to the three sub-passes as an argument
- returns the result of calling the new OverallPass with the
passed Context object
- OverallPass.__init__
- stores various matrices
- sets up global structures (see above)
- instantiates each sub-pass (with a pointer to the OverallPass,
and the passCount for the given sub-pass)
- OverallPass.__call__
- iterates through the list of sub-pass instances calling each
one in turn and tracking whether it reports a visible change
- returns the sum of all the "changed" values (which is in turn
returned by the PassSet to the Context's OnDraw method)
- (Visiting)RenderPass.__call__
- generally checks whether the pass should be rendered or not
(shouldDraw method)
- if not, returns false (no visible change)
- otherwise performs the pass-specific rendering and returns it's
"visible" flag
So, at this point, the RenderPass has been called, we are within the
Context thread (with the lock held), and the OverallPass is initialised.
Each RenderPass defines it's own __call__ method, but the ones
likely of the most interest are the ones derived from
VisitingRenderPass.