It sounds about right, here’s the general steps for the approach I took:
-
We already had a “TopLevelWindow” class for our plugins that was used to handle certain settings like plugin size and such… this is the class I attached the
OpenGLContext
to -
I updated our
TopLevelWindow
to addOpenGLRenderer
to the classes it’s deriving from. I didn’t explicitly usesetComponentPaintingEnabled(false)
because we allow component painting still. HOWEVER, make sure that any components that “fill” the window (including thisTopLevelWindow
) usesetOpaque(true)
. This is what allows the OpenGL rendering to show through when using the other components described below. -
Made a
RenderView
class that inherits bothOpenGLRenderer
andComponent
. These are able to be positioned anywhere in the UI, essentially. This are also markedsetOpaque(true)
. When these are opaque and theTopLevelWindow
is opaque, you can skip drawing theRenderView
background and the OpenGL rendering you’re doing will show through the component painting! -
Added a
Array<RenderView*> renderers
member to theTopLevelWindow
. When theOpenGLRenderer
methods are called likenewOpenGLContextCreated()
,renderOpenGL()
,openGLContextClosing()
theTopLevelWindow
really just iterates that array and calls those methods for each item -
Before actually calling
renderOpenGL()
, we had to ensure that transparency was enabled (glEnable(GL_BLEND)
and friends) and ensure that the viewport was set correctly. The viewport is based on supplyinggetLocalArea()
withRenderView*, RenderView.getLocalBounds()
and multiplying the result byOpenGLContext::getRenderingScale()
. Our plugins allow for resizable windows, so we also had to take our own scale into account as well as that context scale. -
OPTIONAL: Our plugins have features that require adding, removing, and re-ordering the renderers at run-time. Because these operations would come from the main thread, I had to essentially make two
OpenGLRenderer*
queues and add methods that atomically let you add/remove renderers. The methods usestd::atomic_flag
to ensure that only one thing is manipulating the queues at a time, letting us properly set up or tear down the target renderers. For example, if you were to add a renderer while the OpenGL loop is running, you couldn’t call itsrenderOpenGL()
method until calling itsnewOpenGLContextCreated()
method (assuming the renderer needs to allocate buffers etc., which is usually the case). Similarly, renderers in the removal queue have theirrenderOpenGL()
callback called for the last time followed byopenGLContextClosing()
and are then subsequently removed from therenderers
array. -
OPTIONAL: Finally,
RenderView
used the same approach as above and held its own array/queues ofOpenGLRenderer*
. This is mostly because of the previous point: we’ll draw a grid, and RTA display, and an EQ curve all in the same view. If you don’t need that (i.e. eachRenderView
really just draws one thing) then you can just make a custom subclass ofRenderView
each time and write your OpenGL drawing code in the subclass
Sorry if that’s long winded but let me know if you have any other questions and I’ll try to answer them as best as I can! The whole process was very annoying for me so I’d like to help people out in this regard.