Help with Integrate external GUI in to Panda3D (NoesisGUI) (C++)

Hello

I want integrate Noesis GUI. I integrate it, but I have problem with rendering scene.
this is code how I use custom NoesisRegion

	std::string cname = "Noesis Region";
	PT(GraphicsOutput) gpo;
	gpo = window->get_graphics_output();
	static NoesisRegion nsr(gpo, LVecBase4(0.0f, 1.0f, 0.0f, 1.0f), cname);
	nsr.set_sort(20);
	
	PT(OrthographicLens) ole;
	ole = new OrthographicLens();
	ole->set_film_size(2, 2);
	PT(Camera) mCam = new Camera("My Cam 2D", ole);
	NodePath myNode2D(new Camera(*mCam));

	NodePath ndp("myRender2D");
	ndp.set_depth_test(false);
	ndp.set_depth_write(false);
	myNode2D.reparent_to(ndp);
	nsr.set_camera(myNode2D);

GUI is drawn, but problem is, Panda scene from manual is rendered in “inverted” mode.

I post screenshot, you can see on left with tiger (Noesis) in front where panda and whole scene have different render order. On Right side, the code above is commented to not compile and scene with panda is rendered without problem.

What I sould do to render scene correctly?

BTW. I use OpenGL, maybe this information help.

It looks like something may be off with the depth buffer or with depth testing, but it’s hard to say without seeing more code. Is Noesis doing its own OpenGL rendering? If so, it may be that it is leaving depth testing disabled and that Panda is expecting it to still be enabled. It could be an option to use the apitrace tool to obtain a trace of the OpenGL calls and see what might be different in both cases. You can also attach it to a forum post so we could take a look at what is actually going on.

One thing that seems kinda curious is that you are creating a camera “My Cam 2D” and then immediately making a copy of it. Perhaps you meant to simply do:

NodePath myNode2D(mCam);

However, that is unlikely to be the problem here.

thanks, unfortunately I am not able display apitrace tool. (if you mean want-directtools enable)

Yes I use Noesis GLRenderer. And as I playing with configuration file I enable show FPS and 3D scene was rendered OK, maybe you are right that Noesis renderer something turn off. Then question arise, what I need to do turn depth testing enabled back?

Here is my do_cull function (render function) inside NoesisRegion
btw. if I delete or coment code with objects trav, _trav and _net_state, it will be still rendered, I only copied this code from LibRocket integration because they are Panda3D objects, but maybe they are not needed.

PStatTimer timer(get_cull_region_pcollector(), current_thread);

	int pl, pr, pb, pt;
	get_pixels(pl, pr, pb, pt);
	int Width = pr - pl;
	int Height = pt - pb;

	_lens->set_film_size(LVecBase2(Width, -Height));
	_lens->set_film_offset((float)Width * 0.5, (float)Height * 0.5);

	double time = ClockObject::get_global_clock()->get_real_time();
	m_NS_IView->Update(time);
	m_NS_IView->GetRenderer()->UpdateRenderTree();
	m_NS_IView->GetRenderer()->RenderOffscreen();

	m_NS_IView->SetSize(Width, Height);

	CullTraverser *trav = get_cull_traverser();
	trav->set_cull_handler(cull_handler);
	trav->set_scene(scene_setup, gsg, get_incomplete_render());
	trav->set_view_frustum(nullptr);
	
	_net_state = RenderState::make(
		CullBinAttrib::make("unsorted", 0), // unsorted
		DepthTestAttrib::make(RenderAttrib::PandaCompareFunc::M_none),
		DepthWriteAttrib::make(DepthWriteAttrib::M_off),
		ColorBlendAttrib::make(ColorBlendAttrib::M_add,
			ColorBlendAttrib::O_incoming_alpha,
			ColorBlendAttrib::O_one_minus_incoming_alpha
		),
		ColorAttrib::make_vertex()
	);

	m_NS_IView->GetRenderer()->Render();
	trav->end_traverse();
	_net_state = nullptr;

No, that is not what I mean. I mean the apitrace tool.

Well, you shouldn’t make OpenGL draw calls during the cull phase. You should draw during the draw phase. The reason why RocketRegion calls render() during do_cull() is because the RocketRenderInterface is just collecting the generated geometry and recording it with the CullTraverser.

But in your case, it sounds like you are letting Noesis do the drawing entirely. So if Render() is making OpenGL draw calls, you need to make a draw callback and register that on your DisplayRegion. You don’t even need to mess about with the CullTraverser.

Aha,

that means, I need create some function like RenderNoesis() where I put all code from do_cull (without trav objects) and register it in DisplayRegion?
The registration means I put my function in to some function pointer in DisplayRegion?

I used do_cull because from documentation its looks like only one function which do someting with rendering and didn’t find function which do some rendering inside RocketRegion class. I found, but it doesn like they do something important. Maybe I something overlook.

And one thing I forgot mention is, in RocketRegion, everything is looks like rendered from do_cull, because I am inspired wtih RocketRegion.h and cxx file. You can see in do_cull function that everything is called from this function. This is inside do_cull() function

_interface.render(_context, trav);

if you go in to this function, you can discover another codes I have in do_cull function.

But you are right that Cull is for frustrum as it mentioned in manual documentation. Interesting thing is, I render GUI in do_cull and nevertheless it render on display.

Ok, I read C++ references and from that I created custom CallbackObject. (btw in my opinion refence can be better written, and some functions are hidden for example DisplayRegion doesn’t have do_cull function displayed I must open DisplayRegion.h for reference)
After some fight with my CallbackObject I did this: (inside DisplayRegion)

m_CallbackObject = new NsCallbackObject(m_NS_IView.GetPtr(), _lens, this); //(this = NoesisRegion)
set_draw_callback(m_CallbackObject); // (NoesisRegion->)

Am I doing it right?

Unfortunately render order is same as in do_cull() from NoesisRegion. I’ll go test your tool apitrace.

No, not really, not if by “rendered” you mean “drawn”. It does use Rocket’s “render interface” for this, but it does not make any actual draw calls to OpenGL, but rather collects the geometry that Rocket wants to render and adds it for Panda to render during the draw phase later.

You should know that in Panda rendering system, there are two phases of rendering: one is “cull”, which just collects all the geometry that is going to be rendered, and the other is “draw”, which takes the collected geometry (plus any draw callback) and draws them in the appropriate order.

So what _interface.render() is actually doing is invoking libRocket’s render interface, but instead of rendering the geometry that Rocket asks Panda to render, it just collects it for Panda to render later during the draw phase.

The major difference between the Rocket renderer and your approach is that in the Rocket case, it is actually Panda3D doing the rendering, whereas in the Noesis case you are letting Noesis itself do the rendering. To do that you do need to have a callback in the draw phase so that you can use it to invoke Noesis.

What you should do is create a subclass of CallbackObject that overrides the virtual void do_callback(CallbackData *data); method and invokes Noesis to do the draw. It gets passed a DisplayRegionDrawCallbackData pointer.

If it is still not working when you do that, we need to identify which state is being polluted.

Thanks.
Unfortunately it is still not working.

Here is apitrace file from my panda3D sample. Btw. when I use Panda3D app inside apitrace the gui was not shown.

I hope this help, I looked in to frame, and there is Noesis “render section” where are all GL calls.
Panda and Noesis.7z (2.7 MB)

Okay, well, I’m noting from the output that Noesis isn’t actually rendering anything. It’s just preparing things for rendering, up to calling glUseProgram(0) to bind a shader, and then just not following through with an actual draw call. Are there any error messages when running in apitrace? Perhaps it’s failing some step.

I do note that it is disabling the depth write and depth test with glDisable(GL_DEPTH_TEST), though, which explains why it is messing up rendering the next frame, especially since Panda isn’t re-enabling it in the next frame.

I think you could work around this by just calling glEnable(GL_DEPTH_TEST) yourself after Noesis is done rendering. I will think about adding a way to do this automatically in Panda after a draw callback.

Where I should call glEnable? I tried it inside CallbackObject do_callback() function with included opengl. But when I try compile, it throw errors.

Severity Code Description Project File Line Suppression State
Error (active) E2422 defaulted default constructor cannot be constexpr because the corresponding implicitly declared default constructor would not be constexpr Panda and Noesis D:.…\Panda3D-1.10.0\include\pointerToVoid.h 35

Severity Code Description Project File Line Suppression State
Error (active) E2422 defaulted default constructor cannot be constexpr because the corresponding implicitly declared default constructor would not be constexpr Panda and Noesis D:.…\Panda3D-1.10.0\include\weakPointerToVoid.h 28

Severity Code Description Project File Line Suppression State
Error LNK2001 unresolved external symbol __imp__glEnable@4 Panda and Noesis D:.…\Panda and Noesis\NsCallbackObject.obj 1

and apitrace give me this error
btw. when I run sample with apitrace, console is not shown, therefore I can not see if Noesis is loaded or what is going wrong. But here is what I got in apitrace console (screenshot)
apitrace%20error

You would need to add opengl32.lib to your link libraries in your compiler settings if you want to make OpenGL calls. Or you could wait for me to check in a fix in Panda3D itself.

FWIW, if you run apitrace from the console, you should be able to still get command-line output.

Aha, thanks I forgot about that. Now it is working!

I tried animated GUI, and when animation was rendered it make very bad graphic issues. (I post screenshots)
first image is after animation, it looks OK.
First the logo is apeared and then text, but it make this mess on screen as shown on next image.

I tried it resolve with

	glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

This solve problem with mess on screen but this clear everything behind Noesis GUI and add grey backround.
I tried add glClearColor(0.5f, 0.2f, 0.3f, 0.0f); but this not make it transparent, but color is shown.
I have on GLFW sample (where I was use Noesis before test it in Panda) and I have one more function there
glBindFramebufferEXT(GL_FRAMEBUFFER, 0);
but I don’t know if it help. It need glext, and when I compile it, it throw glext.dll is missing. But in GLFW I used GLEW with static linking, but under panda I don’t use GLEW. Maybe I try it tommorow.

The clear settings can be set on the DisplayRegion, by using set_clear_color_active(true), set_clear_color(...), set_clear_depth_active(true), etc. You don’t need to make calls to OpenGL to manage those.

There might be other OpenGL calls that need to be reset in order to restore the state to what Panda (or Noesis) expects. Apitrace is your friend here to find out which OpenGL calls Noesis is actually making.

You shouldn’t need to call glBindFramebufferEXT; this is called by Panda automatically when it finishes rendering to a GLGraphicsBuffer. Does Noesis expect to be rendered into its own framebuffer? In that case you may need to make a buffer using GraphicsEngine::make_output() and add your DisplayRegion there, and render the result to a texture.

But those two main glfunctions must be called to render Noesis correctly (glColorMask and glClear(COLOR_BUFFER_BIT))

if I set up set_clear_color_active(true) for my NoesisRegion, it does same as my glColorClear. If I add it alpha to 0.0f it is still here color background. But it not solve mess on screen.

Btw. another issue I have, if something is animated with EasingMode (Noesis) then it freeze Panda renderer for few seconds. But Noesis is still rendering without any freezeing. I have no clue where can be problem.

Sorry, I forgot to let you know that I checked in a fix a few days ago that may help fix several of the state issues by resetting certain OpenGL state after a draw callback is run:

I also checked in some changes to make the output of apitrace more structured and therefore easier to navigate when debugging a Panda3D program. I also just checked in another change that resets the glColorMask automatically before invoking a draw callback.

Thank you very much. Now Panda with Noesis run without any glitch, even the strange freeze gone.
Except it crash on exit (it crashed on first time), but this is another problem.

Hmm, I tried new Noesis beta and this happens:
:display:gsg:glgsg(error): at 3846 of c:\buildslave\sdk-windows-i386\build\panda\src\glstuff\glGraphicsStateGuardian_src.cxx : invalid value

maybe it is due Noesis change OpenGL renderer?

(on screenshot you can see that grass dodn’t have textures)

Hmm, you should try to find out what changed in Noesis.

You could try setting gl-version 3 2 in Config.prc; it might be the case that Noesis expects a core-only proflie. That’s a shot in the dark, though.