Skip to content

Renderer

Eric Kerfoot edited this page Mar 10, 2017 · 6 revisions

Creating and interacting with the renderer involves instantiating a RenderAdapter object, followed by a RenderScene object and the subsequent objects it is a factory for. At runtime the instance of RenderAdapter is used as the bridge between the renderer and the UI widget (ie. PyQt QWidget) it renders into. The RenderAdapter and getRenderAdapter() declarations in RenderTypes.pxd are given here:

cdef cppclass RenderAdapter:
  u64 createWindow(int width, int height) except+
  void paint()
  void resize(int x, int y,int width, int height)
  RenderScene* getRenderScene()

RenderAdapter* getRenderAdapter(Config* config) except+

The following steps outline the process for starting the renderer based on these interfaces. RenderWidget implements this procedure so it's documentation and code is the best supplement to this description.

Step 1

When the widget is instantiated, getRenderAdapter() is called with a Config object provided which will be retained by the returned RenderAdapter object. When the window is ready to be initialized a value identifying the window is stored in Config under the RenderParamGroup and name "parentWindowHandle" or "externalWindowHandle" depending on the platform:

  • Windows: parent window ID number in "parentWindowHandle"
  • Linux: D:S:W in "parentWindowHandle" where D is the display number, S the screen number, and W the window ID number
  • OSX: window ID number in "externalWindowHandle"

Once this is done createWindow() is called passing in the widget's current dimensions. This instantiates the renderer with the given window parameters and returns the ID number of the internal window object. Other parameters used by the renderer are as follows which typically are specified in the config.ini file read at startup:

  • logfile: path of the logfile to write renderer output to
  • vsync: whether to use vsync or not, value is true or false
  • rendersystem: states which render system to use, possible values are OpenGL (default), D3D9, D3D10, D3D11
  • rtt_preferred_mode: defines a configuration for the OpenGL render system, possible values are FBO (default), PBuffer, Copy
  • plugins: comma-separated list of shared object names stating which renderer plugins to load

Step 2

With the RenderAdapter object created and widget is ready to be made visible, the method createWindow() is called passing in the widget's dimensions and returning the ID number of the internal renderer window. This binds the renderer to the widget through the window ID parameters, and initializes the rendering subsystem and plugins. It is important this is done after the widget is ready (and after a XSync call on Linux) so that the renderer binds to a valid and ready widget.

Step 3

Having initialized the renderer, calling getRenderScene() will return a RenderScene object which will be the interface to the renderer and factory for renderer objects. This must be called exactly once since it returns a new object each time.

With these objects created the renderer is not ready to draw. Whenever the widget receives a redraw signal or whenever a redraw is otherwise required, calling paint() will cause the renderer to draw one frame using cameras drawing into the main window. This must only be called by the main thread of the program, in Qt this is the message pump thread actuating all events and signals. Whenever the widget resizes, resize() must be called passing in the new dimensions.

RenderScene

The purpose of the RenderScene object is to set renderer state settings and instantiate objects representing renderer concepts. All methods of this object must be called in the main thread to ensure thread-safety across all rendering subsystems.

The create*() methods are used to instantiate objects which represent concepts in the scene. Once these are created they are part of the scene and contribute in some way to what is renderer. The most important method is createFigure() which returns a Figure object, specifically which subtype is instantiated is determined by the renderer and the type argument. Having been created, the figure can then be loaded with mesh or other data and then be made visible, it will then be drawn when the renderer performs a draw operation and by default is visible in all cameras.

For example, the follow code can be executed in a script or in the console to create a figure representing a line box, where mgr.scene is bound to the RenderScene object:

@mgr.callThreadSafe
def fig():
	nodes,inds=generateLineBox([vec3(-5),vec3(5)])
	f=mgr.scene.createFigure('fig','Default',FT_LINELIST)
	f.fillData(PyVertexBuffer(nodes),PyIndexBuffer(inds))
	f.setVisible(True)
	return f

This will execute the code to create the figure in the main thread and assign the results to the local variable fig (instead of that name being bound to the function). It's important that fig remain bound to the Figure instance, otherwise the garbage collector would remove the object which would consequently remove the figure from the scene.

Clone this wiki locally