Trying to reproduce a simple demo using CEGUI with Panda3D

First of, I’d like to say that I’m new to Panda3D and CEGUI, but I’m pretty confident in my skills in C++ and Python.

I’m trying to build a simple demo, based on this forum thread: [url]Using CEGUI with Panda in C++]

Unfortunately, I’m not getting the same result from the picture posted by Gogg. Instead, the GUI elements are being shown at the left-bottom corner of the screen, and are limited to a small area around it.

In contrast to his example, I’m using OpenGL only, but that is the main difference between our codes. I’m also using Linux Mint 13, with Panda3D 1.8 and CEGUI 7.9.

I tried changing the scale and position of the NodePath linked to the CEGUI Callback, as well as the size and position of CEGUI’s root window, but there where no changes.

The thing is, I think the viewport for the CEGUI node is limited to a small area to the left-bottom corner of the screen, and the windows frame being rendered into it is being scaled to fit this small area. However, I can’t find a method to resize the area of the CEGUI node to the aspect2d rendering area.

I have already tried using the same code posted by Gogg, only removing the DirectX part of the code, but the result was the same.

Can someone give me an insight on the subject, please?

Here is a sample of the result that I’m having up until now:

If you follow that beautifully drawn red arrow, you will notice a small cursor over the frame’s header. It seems like the CEGUI’s mouse cursor is correctly following Panda3D’s mouse coordinates, but the rendering is distorted, because its area is not occupying the same space of aspect2d.

Here it is my code up until now:

//============================================================================
// Name        : CeGuiPanda.cpp
// Author      : Bruno
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>

#include <panda3d/pandaFramework.h>

#include <panda3d/callbackData.h>
#include <panda3d/callbackNode.h>
#include <panda3d/callbackObject.h>

#include <panda3d/pgButton.h>

#include <panda3d/mouseWatcher.h>

#include <CEGUI/CEGUI.h>
#include <CEGUI/RendererModules/OpenGL/CEGUIOpenGL.h>
#include <CEGUI/RendererModules/OpenGL/CEGUIOpenGLRenderer.h>

// Panda will call this every frame to render CEGUI
class CeGuiRenderInterfaceCallback : public CallbackObject
{
public:
	ALLOC_DELETED_CHAIN(CeGuiRenderInterfaceCallback);

	virtual void do_callback( CallbackData * aCallbackData )
	{
		CEGUI::System * lpCeGuiSystem = CEGUI::System::getSingletonPtr();
		if ( lpCeGuiSystem )
			lpCeGuiSystem->renderGUI();

		if ( aCallbackData )
			aCallbackData->upcall();
	}
};

PT(WindowFramework)gpPandaWindow = NULL;
PT(MouseWatcher) gpMouseWatcher = NULL;

AsyncTask::DoneStatus main_task( GenericAsyncTask* task, void* data )
{
	if (  gpMouseWatcher->has_mouse() )
	{
		if ( gpPandaWindow )
		{
			GraphicsWindow *gwin = gpPandaWindow->get_graphics_window();
			if ( gwin )
			{
				int mouse_x = gwin->get_pointer( 0 ).get_x();
				int mouse_y = gwin->get_pointer( 0 ).get_y();
				CEGUI::System::getSingleton().injectMousePosition( mouse_x, mouse_y );
			}
		}
	}
	return AsyncTask::DS_cont;
}

// mouse events:
void mouse_down( const Event *ev, void *data )
{
	CEGUI::System::getSingleton().injectMouseButtonDown( CEGUI::LeftButton );
}
void mouse_up( const Event *ev, void *data )
{
	CEGUI::System::getSingleton().injectMouseButtonUp( CEGUI::LeftButton );
}

int main( int aArgumentsCount, char* aArguments[] )
{
	//open a new window framework
	PandaFramework lPandaFramework;
	lPandaFramework.open_framework( aArgumentsCount, aArguments );
	//set the window title to My Panda3D Window
	lPandaFramework.set_window_title( "My Panda3D Window" );
	// We want to hide the OS cursor since CEGUI has its own cursors.
	// (We could also hide CEGUI cursors instead...)
	WindowProperties props = WindowProperties::get_default();
	props.set_cursor_hidden( false );
	//open the window
	gpPandaWindow = lPandaFramework.open_window( props, 0 );

	// Load some model to test the CEGUI window semitransparency
	NodePath smiley = gpPandaWindow->load_model( lPandaFramework.get_models(), "models/panda" );
	smiley.reparent_to( gpPandaWindow->get_render() );
	gpPandaWindow->get_camera_group().set_pos( 30, -30, 30 );
	gpPandaWindow->get_camera_group().look_at( 0, 0, 0 );

	CEGUI::OpenGLRenderer::bootstrapSystem();

	PT(CallbackNode)lCallbackNode = new CallbackNode("CEGUI");
	lCallbackNode->set_draw_callback( new CeGuiRenderInterfaceCallback() );
	NodePath lNodeCegui = gpPandaWindow->get_aspect_2d().attach_new_node( lCallbackNode );

	// Let's add a task that we'll use to keep CEGUI updated about the mouse cursor
	// position.
	PT(GenericAsyncTask)main_task_object = new GenericAsyncTask("main task", &main_task, (void*) NULL);
	AsyncTaskManager::get_global_ptr()->add( main_task_object );

	gpMouseWatcher = (MouseWatcher*) gpPandaWindow->get_mouse().node();

	// For this sample code we are just gonna setup two input events, mouse button left up and
	// down, it's enough to drag the window around.
	gpPandaWindow->enable_keyboard();
	lPandaFramework.define_key( "mouse1", "mouse1", &mouse_down, (void*) 0 );
	lPandaFramework.define_key( "mouse1-up", "mouse1-up", &mouse_up, (void*) 0 );

	//here is room for your own code

	CEGUI::DefaultResourceProvider* rp = static_cast<CEGUI::DefaultResourceProvider*>( CEGUI::System::getSingleton().getResourceProvider() );

	const CEGUI::String lString = "/home/bruno/Programas/CEGUI/CEGUI-0.7.9/datafiles";

	rp->setResourceGroupDirectory( "schemes", lString + "/schemes/" );
	rp->setResourceGroupDirectory( "imagesets", lString + "/imagesets/" );
	rp->setResourceGroupDirectory( "fonts", lString + "/fonts/" );
	rp->setResourceGroupDirectory( "layouts", lString + "/layouts/" );
	rp->setResourceGroupDirectory( "looknfeels", lString + "/looknfeel/" );
	rp->setResourceGroupDirectory( "lua_scripts", lString + "/lua_scripts/" );

	CEGUI::Imageset::setDefaultResourceGroup( "imagesets" );
	CEGUI::Font::setDefaultResourceGroup( "fonts" );
	CEGUI::Scheme::setDefaultResourceGroup( "schemes" );
	CEGUI::WidgetLookManager::setDefaultResourceGroup( "looknfeels" );
	CEGUI::WindowManager::setDefaultResourceGroup( "layouts" );
	CEGUI::ScriptModule::setDefaultResourceGroup( "lua_scripts" );

	CEGUI::Imageset& wlis = CEGUI::ImagesetManager::getSingleton().create( "TaharezLook.imageset", "imagesets" );
	CEGUI::Scheme& sch = CEGUI::SchemeManager::getSingleton().create( "TaharezLook.scheme", "schemes" );

	CEGUI::System::getSingleton().setDefaultMouseCursor( "TaharezLook", "MouseArrow" );

	CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();

	CEGUI::Window* myRoot = wmgr.createWindow( "DefaultWindow", "root" );

	CEGUI::System::getSingleton().setGUISheet( myRoot );

	CEGUI::FrameWindow* lpCeGuiFrame = static_cast<CEGUI::FrameWindow*>( wmgr.createWindow( "TaharezLook/FrameWindow", "testWindow" ) ); //TaharezLook/FrameWindow

	myRoot->addChildWindow( lpCeGuiFrame );

	lpCeGuiFrame->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.25f, 0.0f ), CEGUI::UDim( 0.25f, 0.0f ) ) );
	lpCeGuiFrame->setSize( CEGUI::UVector2( CEGUI::UDim( 0.5f, 0.0f ), CEGUI::UDim( 0.5f, 0.0f ) ) );
	lpCeGuiFrame->setVisible( true );
	lpCeGuiFrame->setAlpha( 0.5f );
	lpCeGuiFrame->setAlwaysOnTop( false );
	lpCeGuiFrame->setText( "Hello World!" );

	//do the main loop, equal to run() in python
	lPandaFramework.main_loop();
	//close the window framework
	lPandaFramework.close_framework();

	return ( 0 );
}

Perhaps you need to tell CEGUI what the size of the Panda window is?

I’ve been trying to do that in the past few hours, but I’m having two major problems:

I don’t know how to extract the size of the rendering area from the Panda3D, but I’m guessing it’s in the aspect2d’s Camera/Lens object. However, I “can’t”/“don’t know how” extract those from it. When I try to retrieve the aspect2d->node, I’m getting a PGTop pointer, which reads/processes some mouse events.

The second major problem is that I’m still reading the CEGUI docs, but so far, I haven’t found a clue as to where to change the drawable area of the windows.

I’m guessing it’s the root that changes that property, but so far I failed to obtain any changes (except making the whole drawing area disappear).

Right now, I’m trying something like:

CEGUI::Window* myRoot = wmgr.createWindow( "DefaultWindow", "root" );

	std::cout <<
		"Width(" << myRoot->getArea().getWidth().d_scale << "," << myRoot->getArea().getWidth().d_offset << ")\n" <<
		"Heigth(" << myRoot->getArea().getHeight().d_scale << "," << myRoot->getArea().getHeight().d_offset << ")\n" <<
		"Pos.x(" << myRoot->getArea().getPosition().d_x.d_scale  << "," << myRoot->getArea().getPosition().d_x.d_offset  << ")\n" <<
		"Pos.y(" << myRoot->getArea().getPosition().d_y.d_scale  << "," << myRoot->getArea().getPosition().d_y.d_offset  << ")\n" <<
		"Size.x(" << myRoot->getArea().getSize().d_x.d_scale  << "," << myRoot->getArea().getSize().d_x.d_offset  << ")\n" <<
		"Size.y(" << myRoot->getArea().getSize().d_y.d_scale  << "," << myRoot->getArea().getSize().d_y.d_offset  << ")\n"
	;

	
	myRoot->setArea(
		CEGUI::UVector2( CEGUI::UDim(0.0f,0.0f), CEGUI::UDim(0.0f,0.0f) ),
		CEGUI::UVector2( CEGUI::UDim(2.0f,0.0f), CEGUI::UDim(2.0f,0.0f) )
	);

If the scale value for myRoot->setArea() is < 1.0f, there are visible changes, and the area gets smaller indeed, but higher values don’t reflect this, even when I set myRoot->setMaxArea to values higher than 1.0f.

So I managed to find out what was missing from the original demo.

It turns out that the glViewport was smaller than the Panda3D frame window.

In order to fix this, I add the following code:

void EventCallback_OnWindowsResize(const Event * aEvent, void * aData)
{
	if( !gpPandaWindow )
		return;

	CEGUI::Renderer * lRenderer = CEGUI::System::getSingleton().getRenderer();

	if( !lRenderer )
		return;

	int lWidth = gpPandaWindow->get_graphics_output()->get_x_size();
	int lHeight = gpPandaWindow->get_graphics_output()->get_y_size();
        //Fix the glViewport
	lRenderer->setDisplaySize( CEGUI::Size( lWidth, lHeight ) );
        //if you don't re-set the position of the CEGUI root window, then the inside frame won't be able to be moved to the full right side of the window.
        //gpCeGuiRoot was called myRoot before, and was local. Now It's global, so it can be called in the event.
	gpCeGuiRoot->setPosition( CEGUI::UVector2( CEGUI::UDim( 0.0f, 0.0f ) , CEGUI::UDim( 0.0f, 0.0f ) ) );
}

After that, I register this event with the following code:

lPandaFramework.get_event_handler().add_hook( "window-event", EventCallback_OnWindowsResize, NULL );

Now, even thought this works, there is an drawback to this approach. When you re-size your screen, the GUI elements will resize with it. This is probably what one would not always desire, since sometimes you just want to make the playing field larger, while keeping the GUI with the same size.

I don’t think there is an easy way to achieve this, however, and if something like that is desired, then every GUI element should be resized and re-positioned to keep the same proportion.

There is yet another problem with my approach, and that’s because of the “window-event” event handler, that will call the resizing function everytime some event happens with the window.

Unfortunately, I don’t think it’s possible to filter this event to receive just resize events in C++.

Anyway, thanks for everyone who dedicated sometime reading my posts.