CEGUI in Panda3d (WIP)

No, the scene graph sort order is not respected in any bin other than ‘unsorted’, because of course, these other bins define their own sorts, which overrides. (You could try to make a case than in the case of equal Z (or Y) values, back-to-front should preserve the scene graph order, but this wouldn’t work, because the Z values are floating-point and thus can’t ever be reliably equal–this is the very definition of Z fighting.)

I still say (2) is the correct case, and it is surely what CEGUI expects you to do. Nothing else makes sense. I’ll bet you a dollar that this is when the OpenGLRenderer does too.

Incidentally, you don’t need to explicitly increment the sort order while you are building up the scene graph, because the scene graph is defined to preserve nodes in the order in which they are attached, unless an explicit sort order is given which contradicts this attachment order.

David

It’s a little more complex than that. This is a list of quads’ Z-order as Cegui passes them in:

0.999
0.999
0.999
0.999
0.999
0.999
0.998
0.998
0.998
0.997
0.997
0.997
0.997
...

Within the same Z value, the order should be back-to-front, but as I understand, there is no guarantee from Cegui that the Z values will be decreasing like that.

(2) is bad because it throws away the Z order information, and hopes that the layers (sets of quads with the same Z value) arrive in the right order.

The OpenGL renderer sorts the quads by their Z values (stable sort), then draws them in this order. Quads just happen to be already sorted like that.

Another option would be to use the Z value as the sort parameter for the ‘unsorted’ bin, but Z is float, so we need to convert it to int, and that relies on our knowledge of the smallest step (which is 0.001). It is just easier to keep them as float and use them as the Y coordinate, incrementing it by say 0.000001 (or something like 1.0 / (2 << 16)).

The OpenGL renderer also needs to keep a list of quads because must render them every frame. In our case, Panda takes care of that, so the renderer doesn’t store the list of quads, which makes in simpler.

Here is a counterexample. In this case, both the Y values and the sort order matter. First, Y is compared, then the sort order.

import direct.directbase.DirectStart
from direct.showbase.DirectObject import *
from direct.task.Task import Task
from pandac.PandaModules import *
import pdb

b2f_node = render2d.attachNewNode('back2front')
b2f_node.setBin('transparent', 50)      # Back to front order
cm = CardMaker('')

# First, y coords are compared. Lower Y -> in front.
# If y is the same, sort order is compared. Higher sort -> in front.

green_card = NodePath(cm.generate())
green_card.setTransparency(TransparencyAttrib.MAlpha);
green_card.setColorScale(0, 1, 0, 0.8)
green_card.setX(-0.5)

blue_card = NodePath(cm.generate())
blue_card.setTransparency(TransparencyAttrib.MAlpha);
blue_card.setColorScale(0, 0, 1, 0.8)

# Play with these values to see how it works
green_card.setY(1)
green_card.reparentTo(b2f_node, 4)

blue_card.setY(1)
blue_card.reparentTo(b2f_node, 3)

while True:
    taskMgr.step()

This is the behaviour I’d like to see, but I can’t reproduce this in the renderer, and I don’t understand why.

Really? That’s just weird. I mean, really weird. But I guess that’s why they call him Crazy Eddie. :slight_smile:

I suppose you can’t just do a stable sort by their Z value, like the OpenGLRenderer does, before building up the GeomVertexData? This would work with all of the quads that were added in a single batch, but it wouldn’t work if you had to intermix with other quads that were added in a previous frame. But maybe that’s not an issue, since it would also be an issue with (2), and you don’t mention that as a problem.

From what you’ve described, you have to make one of three assumptions about CEGUI:
(1) The smallest increment between Z values is 0.001.
(2) You can choose a Z increment that balances the minimum increment between layers, the maximum number of quads in a layer, and the depth precision of the user’s hardware.
(3) Quads will be issued in increasing Z order.

So, which of these is the safest assumption to make?

David

It’s not actually defined behavior. The CullBinBackToFront class uses a standard sort, not a stable sort, which means that values of equal Z will have an undefined sort order relative to each other. In your example, they happen to be coming out in their original positions, but that is not necessarily the case always, as you are discovering.

One answer is to replace the sort call at cullBinBackToFront.cxx line 90 with a stable_sort call instead. But, again, this is just weird, because you’re relying on floating-point numbers evaulating equal to each other. It’s likely to work in this contrived circumstance, where you have an orthographic projection matrix and an identity worldview matrix, and the Z values are specifically chosen to be multiples of 0.001, but in general it is a perfectly meaningless condition. I guess it wouldn’t do any harm to make this a permanent change, but it seems as hacky to me as any of your other options.

David

One question:

Performance. Is it faster then DirectGUI/the panda’s 2d enviroment?

It should be as fast as DirectGui. It displays GUI elements in a similar way (GeomNodes with textures under a 2d camera).

I’m working on generating a Python interface to it to test with a dynamically-updated GUI (like a countdown timer or something.). Should have something to show this weekend.

Time for a beta release! (I’m not sure how to package it. Let me know if I’m missing anything).

Status: it works well enough.

TODO:
Cleanup the code (formatting, etc.)
Bug: select some text in the multiline textbox, and scroll down. Sometimes, the selection background does not get updated properly. This has to do with quad caching, I’m working on it.

Usage

This code gets you something like the screenshots above:

...
# Init Cegui
CeguiSupport.init(base.win,
                  base.mouseWatcher.getParent(),
                  render2d)

# Create GUI

root = cegui.WindowManager.getSingleton().loadWindowLayout('FontDemo.layout')
sys = cegui.System.getSingleton()
sys.GUISheet = root
...render loop...

Installation

Note: this is for Linux/Mac, Ppremake only (see questions below).

You’ll need CEGUI itself and Python bindings for it. Compile and install the following:

CEGUI 0.6.2:
prdownloads.sourceforge.net/cray … z?download

CEGUIPython:

svn co http://svn.projectxenocide.com/xenocide/trunk/cegui/ScriptingModules/CEGUIPython CEGUIPython

I’ve had some trouble compiling CEGUIPython on a Mac, I hope it will be easier on Linux. I’m not sure if it is currently matching CEGUI 0.6.2. I had to fiddle with it a bit to get it to compile.

Patch for the Panda source tree:

pastebin.ubuntu.com/154468/

You’ll also need to copy the datafiles directory from CEGUI/Samples/datafiles into panda/src/cegui/datafiles. This contains textures, etc, which the patch doesn’t contain.

Compiling:

cd dtool
ppremake
make && make install
cd ../panda/src/cegui
ppremake
make && make install
genPyCode libpcegui

This exposes a single Python method, CeguiSupport.init, to initialize the renderer and the input injector. The rest is done in Python, using the CEGUIPython bindings.

It is also usable from pure C++, see cegui.cxx for an example.

Now, my questions.

  1. Cegui requires a method that returns the maximum texture size supported by the renderer. Where do I get this in Panda?

Packaging questions:

  1. I only have support for ppremake right now. What else needs to be supported? makepanda? How does the Windows build work?

  2. What are metalibs for? Do I need to add anything there?

  3. How do we want to include CEGUI? Should we link to it statically?

I guess CEGUI and CEGUIPython will go in the thirdparty dir. I was told that ODE is linked to statically, and is one of the thirdparty packages. However, mine is compiled with the ODE that was installed separately. What is the proper way to do things like that?

What else am I missing?

  1. GraphicsStateGuardian::get_max_texture_dimension(). If you don’t know the particular GSG that will be in use, you can use GraphicsStateGuardian::get_default_gsg() to get a pointer to the default GSG object that has already been created (if any; note that this pointer might be NULL).

  2. makepanda support will be necessary for full integration; if you can provide this as part of your own efforts, so much the better. The windows build works the same as the Linux and OSX builds, using either ppremake or makepanda.

  3. metalibs are composites of multiple smaller libraries; e.g. pgraph, putil, gobj, etc. get combined into libpanda. If your library stands alone, it doesn’t need to meddle with metalibs.

  4. How is CEGUI normally built? Linking to it in the normal way would be best. If it’s normally linked statically, perfect; or dynamically is OK too. Really, static vs. dynamic linkage makes no difference at all to the build system.

David

I’m stuck again!
I’m compiling in Ubuntu with Ppremake, and a strange error appeared.
I’m parenting my InputHandler under a MouseAndKeyboard node. This produces the error:

:dgraph(warning): Ignoring mismatched type for connection pixel_size between MouseAndKeyboard keyboard_mouse and InputHandler simple_input

The mismatch is because MouseAndKeyboard’s output type is EventStoreVec2, as it should be, but InputHandler’s input type is none.

To figure out what’s going on, I’m printing EventStoreVec2::get_class_type() in MouseWatcher constructor and in my InputHandler’s constructor, as well as every frame. The MouseWatcher correctly prints EventStoreVec2, and InputHandler prints “none”.

It used to work before (on a Mac). I rebuilt everything - same thing. I’m linking to the same libs as the tform code does.

What am I missing?

Thanks,

Nik

Is the init_type of your class called (usually in config_blah.cxx) ?

Yes, init_type is called.
I’m solving the problem temporarily by using

TypeRegistry::ptr()->find_type("EventStoreVec2")

instead of

EventStoreVec2::get_class_type()

, but I still don’t understand what’s going on.

EventStoreVec2 is a template class. Template classes are sometimes problematic because there might be multiple instances of the same class in different parts of the code, so that EventStoreVec2::get_class_type(), as called in one module, is not the same method as EventStoreVec2::get_class_type(), as called in another module.

This is I think is what’s causing problems. To avoid it, try adding an explicit call to EventStoreVec2::init_type() in config_tform.cxx.

David

Just thought I’d poke this thread, show my interest, and ask if you’d like any help with anything. Just became interested in porting our old (CS based app) CEGUI theme to something in out new panda3d app, and I stumbled across this. Sounds like you’re very close to something usable; I’d love to help out where possible.

Any sort of status update on the build issue?

Morgul, sorry for not replying (somehow I didn’t receive a reply notification).

Here is the status:

I’m happy with the C++ code that I have now.

The Python wrapper was more problematic. There are several Python wrappers for CEGUI out there. The Python-Ogre project seems to be the only one that’s still maintained.

The problem with it is that it depends on Ogre3d, so using these wrappers pulls in Ogre as a dependency. However, it should not be a problem to remove the Ogre dependencies out of the wrapper libs.

Anyways, I got it to work today with the Python-Ogre wrappers. I’ll package what I have now and send out a patch and more info later today.

Nik

Here is a patch:
http://paste.ubuntu.com/246744

Instructions for ppremake:

  1. [SEE POST BELOW ON HOW TO SPEED THIS UP]
    Get Python-Ogre. Start with this one, because you’ll need to link to the CEGUI lib that it uses.
    Download and build (or use the Windows binary release). Building takes a while (hours).
    Run the demos (at least demos/cegui/Demo_CEGUI_NewGui.py should work).

  2. Get the Silly Image Codec. Compile and install.

  3. Apply the patch to the Panda3d source tree.

The parts for MyConfig.pp will probably fail. Find the CEGUI library and headers that Python-Ogre uses, and set CEGUI_IPATH and CEGUI_LPATH to point there.

In my case, I have this in MyConfig.pp:

#define CEGUI_LPATH /Users/nik/Library/Frameworks/CEGUI.framework/
  1. Verify that CEGUI libs are found by PPremake:
cd dtool
ppremake
# Results should contain "+ CEGUI"
  1. Build and install libpcegui for Panda3d:
cd panda/src/cegui
ppremake
make
make install
genPyCode
  1. Copy the datafiles directory from the Python-Ogre demos directory into panda/src/cegui.

  2. Run python_test.py and ogre_font_demo.py - you should have a working CEGUI.

Those who use Panda3d from C++ don’t need the Python wrappers from Python-Ogre, so they can skip step 1 and build CEGUI from source instead.
It would be nice if someone could go through this and post feedback.

Thanks,

Nik

(edit: added step 6)

I’ve modified Python-Ogre to not depend on Ogre. The wrappers can now be built much faster (half an hour on a slower machine).
Here is how to build it from source on Ubuntu 9.04 or OSX:

mkdir ~/pycegui; cd ~/pycegui
# Checkout Python-Ogre tree from SVN
svn co https://python-ogre.svn.sourceforge.net/svnroot/python-ogre/trunk/python-ogre python-ogre -r 984

# Build the prerequisites. They get installed in ~/pycegui/root/usr/lib/
python python-ogre/BuildModule.py -r -b cmake gccxml boost pyplusplus pygccxml

Here is a patch for the Python-Ogre tree: http://paste.ubuntu.com/248927/.
Apply it to the ~/pycegui/python-ogre folder.

Then build CEGUI, generate and build Python wrappers:

python python-ogre/BuildModule.py -r -b cegui
# Generate wrappers
python python-ogre/BuildModule.py -g cegui
# Compile wrappers - this takes a while.
python python-ogre/BuildModule.py -c cegui
# If there are no errors (a few warnings are normal), install:
python python-ogre/BuildModule.py -b install

Now you should see a file ~/pycegui/root/usr/lib/python2.6/site-packages/ogre/gui/CEGUI/cegui.so

Export the env. vars:

export LD_LIBRARY_PATH=/home/$USER/pycegui/root/usr/lib/:$LD_LIBRARY_PATH
export PYTHONPATH=/home/$USER/pycegui/root/usr/lib/python2.6/site-packages:$PYTHONPATH

Now follow steps 2…7 in the post above to setup tha panda tree. Panda’s CEGUI_LPATH must point to ~/pycegui/root/usr/lib/.

Then run the test:

cd panda/src/cegui
python ogre_font_demo.py
OR
python python_test.py

Reference:http://wiki.python-ogre.org/index.php/LinuxBuildV2

Thanks,

Nik

Hm, what about using interrogate to create the interfaces, like we do with most Panda stuff? Does the user of PandaCegui need to use the Cegui original classes?

I’ve thought about it. There are about 160 classes to be wrapped, with many properties. It’s not a trivial task.

The user of CEGUI does need all of these classes to interact with it.

We could search for cegui.so in CEGUI_LIBS as a way to detect if the Python wrappers for CEGUI are installed.

Hum, well, what about putting the Cegui includes between BEGIN_PUBLISH and END_PUBLISH blocks? At least cegui follows our Python coding convention.