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.