Porting UI to JUCE within existing OpenGL application

Hi everyone,

I’m investigating using JUCE primarily as a GUI system for an existing product I am adding features to. nanovg is currently being used for the UI and works fairly well, though the GUI system that’s implemented is far from complete. As of a few weeks ago, nanovg is no longer maintained by the author.

All rendering is done via OpenGL. The application uses VR (rift and vive) and allows for video editing/sequencing. We have our own renderer and need to maintain access to the GL API, ideally not through wrapper functions. We also need to be able to set the GL version (4.x).

Can the JUCE UI run within 4.x GL, and if so, can it coexist with raw OpenGL code that isn’t calling its wrapper functions? I understand we’d hit some state pollution, but could work around that.

Cheers,
James

The answer isn’t totally straightforward because JUCE doesn’t draw directly on the OpenGL thread. The other difficult part is that JUCE naively approaches OpenGL by treating it like a rasteriser, and only offers a very limited subset of functionality to work with it. And it barely wrangles the GL functionality that most GPU-based drawing apps would use.

If you’re intending to mix OpenGL GUI elements and JUCE-based GUI elements, then you’ll have to jig your own renderer to do that. Unfortunately that would be extremely complicated. IMO you could solve that by somehow wrapping nanovg in a juce::LowLevelGraphicsContext, and that should solve running GL calls directly in Component::paint callbacks and probably get you most of the way there.

2 Likes

Thanks for the reply. I think what we’re looking to do is to remove nanovg completely and replace it with a JUCE-based GUI. The non-gui OpenGL rendering would happen inside of the main window (think like After Effects or Unity) with the timeline, panels, etc. being driven by JUCE. Do you think this would be possible while still using GL directly and not through JUCE’s GL code?

Just saw you’re from Ottawa :wink: I grew up there and moved away a few years ago to Sudbury. Miss it tons.

I always found the audio industry to be a small world. It’s starting to feel a hint smaller now. :slight_smile: Were you working in software or games in Ottawa at the time?

Yep, I’m extremely familiar with 3D renderers, and secondarily how that can fit with JUCE.

Assuming this means tossing away all JUCE GUI code, have a look here in the OpenGLDemo’s renderOpenGL() function for a hint towards what you’re looking at accomplishing.

Basically this would still give you a JUCE-based window to control (assuming you need that), a high-level juce::Component to just kind of house all the things, then the barebones OpenGLRenderer to run all GL calls you would want.

1 Like

I had a similar case to solve on an embedded Linux. I had to have a normal Juce UI but also some video handling done by OpenGLES (not supported by Juce under Linux). I ended with separated classes which take getWindowHandle() pointers to some components. So I can draw on selected components using OpenGLES and all normal components are painted by native Juce methods. You could construct a standard Juce UI (even without OpenGL) and do your customised rendering using OpenGL 4.X directly on selected components.

1 Like

@MBO could you elaborate a bit more about how you handled that separate OpenGLES & JUCE software rendering? I find myself in a similar situation for embedded Linux. Also I thought OpenGL & OpenGLES were the same so just had to add only performance primitives when on an embedded environment.

First, I unchecked juce_opengl in Projucer - otherwise there are names conflicts between OpenGL and OpenGLES. Probably if you move OpenGLES code to an external library it is possible to overcome it but in my case software rendering works ok.

Then I created normal components like buttons, labels, lists etc. and some dummy components. You need to take a window handle from such dummy component by getWindowHandle and redirect it to OpenGLES to create a surface to render. In my case I use EGLNativeWindowType function, then eglCreateWindowSurface and eglCreateContext. After I make my context current (eglMakeCurrent) I can render all needed things just using raw OpenGL(ES) methods by shaders and programs.

1 Like

Much appreciated, will get my hands dirty with it soon!