Trouble Sorting Transparent Geometries in Lemur

I was having some trouble with transparency when using Lemur in a perspective camera. Basically some things were being drawn in the wrong order. I have my scene for that particular viewport using the Transparent bucket, but some items were being rendered front to back rather than back to front.

Basically if I had a Container with a background child elements that also had a background would render before the container’s background so you could see through the container’s background where the child element was transparent. Upon investigation it would appear that this was happening when the LayerComparator found two elements to have an equal effectiveLayer and thus passed the sorting on to the TransparentComparator which apparently doesn’t seem to work so well when objects are fairly close together, 0.01 apart. It didn’t start to work until I separated the objects by 20 on the z-axis, but that didn’t look all that great.

In looking at the TransparentComparator I found that there were two methods written for determining the distance to the camera, distanceToCam and distanceToCam2, but only distanceToCam was being used when sorting so I tried a LayerComparator that fell back on a custom GeometryComparator that was just a carbon copy of TransparentComparator that used distanceToCam2 instead of distanceToCam and that had significantly better results, although there were still a couple of instances where the render order wasn’t correct.

So I wrote a new LayerComparator that overrode calculateEffectiveLayer so it read:

protected float calculateEffectiveLayer(Geometry g) {
    Integer childLayer = g.getUserData(LAYER);
    float layer = childLayer != null ? (childLayer + 1) : 1;

    for( Spatial s = g.getParent(); s != null; s = s.getParent() ) {
        Integer i = s.getUserData(LAYER);
        // I'm not sure skipping a null layer is right but it's 
        // been this way for a while without obvious issue.  It
        // seems like skipping it might cause two separate objects
        // with sparse hierarchies to sort incorrectly.  I'm
        // leaving it for now.
        //if (i == null)
        // Should really base the divisor on the number
        // of children... since right now if we exceed more
        // than 10 layers under a parent then we overflow
        //layer = layer * 0.1F;
        layer += i != null ? (i + 1) : 1;

    return layer;

I just got rid of the multiplication by 0.1f and reverted it to where it would add 1 to the layer or set the layer to 1 if no layer was set on the object. This, at least so far, has completely solved the issue.

There is no accurate way to sort geometry. There just isn’t and never will be. It’s trivial to create cases that fail for any approach used… even with just two triangles. In fact, it’s trivial to create a case for two triangles where there is no correct sort order.

…that’s why Lemur let’s you use layers to force the sort order for things like background objects, etc… I’m not sure it’s always exposed properly, though. But that’s the answer, set the layers for the things you want to be sorted a specific way relative to each other.

I wasn’t really asking a question, I just posted this for anyone that might be experiencing a similar problem, perhaps this solution will work.

All my components have a layer set, the problem was that the comparator ignored nodes with no layer set, so the background of the child element had the same effectiveLayer as the background of the container. When I added a layer to the parent nodes things got even worse, but removing the multiplication by 0.1 in the comparator fixed it.

By doing that, you can no longer support nested layers properly is all.

I have multiple nested layouts, is that what you mean?

Each parent child layering is supposed to get its own separate sorting. That’s the point of moving the decimal place as you go up.

Layers are sorted GLOBALLY because it cannot be otherwise. But quite often, you want the layers of this label to sort relative to one another but the label itself to sort relative to its container and so on. Else your whole UI from root to lowest child has to know what all of the layers are.

So, container layer = 1
Label layer = 2
Label.background layer = 1
Label.shadow layer = 2
Label.text layer = 3
…and so on.

Buy the effective layers would be

Container: 2
Label: 2.2
Label.background: 2.22
Label.shadow: 2.23
Label.text: 2.24

Say you have:

Container1: 1
Container1Background: 1
Label1text: 3
Label1Background: 1

Container2: 1
Label2text: 3
Label2Background: 1

then you get:
Container1: 2
Container1Background: 2.2
Label1text: 2.4
Label1Background: 2.2

Container2: 2.2
Label2text: 2.24
Label2Background: 2.22

Note Label1Background 2.2 == Container1Background 2.2

So Label1 is not a child of Container1?

Then all bets are off, really.

Label1 is a child of Container1, but only components are given layers by default, you have to manually assign layers to Elements like labels and containers.

Oh I see what I did, I was setting the layer for each container, but not the child elements of each container. I did a lot of stuff like Container.addChild(new Label()), but instead I need to do

Label label = new Label("");
label.setUserData("layer", #);

Thanks for the assist BTW. I wonder if this could be done automatically in the layout when adding a child it just sets the layer on the child being added to its own layer + 1.

So there would be a set layer method in Panel that sets the layer and container would override this to propegate the new layer + 1 to its child elements.

Well, if the parent has a layer then all of the children can just be a layer… but somehow that layer needs to be sorted within the component stack from the parent or else they will compete.

Mostly, z-sorting is enough in the 2D UI. Things only get weird in 3D and they get pretty weird anyway when dealing with 2D objects in the 3D space. (I had to deal with this in Mythruna a lot, too, as my menu pages are all stacked 3D objects… but at least the player can’t turn their head much and the object don’t rotate much.)

For the books, I ended up rendering a 2D UI off screen and just texture mapping it onto the curved book faces.

For floating UI elements, I think there is no perfect automatic way… the caller may always need to set the layers themselves on some level.