Render selection marker above all other objects / Specify render order

Hey, :slight_smile:

I’m working on a game which you can compare to AgeOfEmpires or any other strategy game where you can control an army.

If I select one or more units, there are markers drawn around them:







But, as you can see, sometimes the terrain tiles “overlap” the markers so it doesn’t look very nice when moving the units around.



Is there any way to specify, that the selection markers should be drawn after all other stuff (But before the GUI of course). I’ve already searched the forums and Google, but all I found was the hint to the render buckets, but I didn’t have success with these ones.



Info: The Marker is a Geometry, created from a Quad. Here’s the function that generates it:

[java]public static Spatial generateSelectionMarker(String textureFilePath, float width, float height){

Spatial selectionMarker = new Geometry(“selection_marker”, new Quad(width, height));

selectionMarker.rotate(JMonkeyUtil.getQuaternion_X(-90));

selectionMarker.setLocalTranslation((width / -2), Map.JUST_ABOVE_THE_SURFACE_Y, (height / 2));

Material material = MaterialFactory.generateLightingMaterial(textureFilePath);

material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);

material.getAdditionalRenderState().setDepthWrite(false);

selectionMarker.setMaterial(material);

selectionMarker.setQueueBucket(Bucket.Transparent);

selectionMarker.setShadowMode(ShadowMode.Receive);

return selectionMarker;

}[/java]



I hope you understand the question and that the answer isn’t too obvious. :smiley:



Yours, destro :slight_smile:

I don’t really know how to handle the drawing order. But look at this thing, maybe it can help you out: projective-texture-mapping/

http://hub.jmonkeyengine.org/javadoc/com/jme3/renderer/queue/RenderQueue.html#setGeometryComparator(com.jme3.renderer.queue.RenderQueue.Bucket,%20com.jme3.renderer.queue.GeometryComparator)



You can probably do a “custom google search” in the upper right for GeometyComparator for more information.

Rendering them above other objects seems like a problematic idea. Not sure if you’ll ever be able to cover all instances.

Units coming over a slight hill… you’ll see the ring but the unit is still partially covered (i.e. you shouldn’t see the ring yet)



Using projective texturing is also not going to work for you, due to the number of units that could potentially be selected. Or if you use a single projector… you might as well use the next idea…



Maybe a possible solution would be texturing splatting. Also problematic… but it may be less problems than the alternative. Texture splatting is going to have the same issue as projective texturing, just less expensive in the long run (no extra render passes). You’ll constantly have to redraw you’re alpha map… but you’ll have to do the same with the colormap for projective texturing if you go the single projector method.



/shrug… some ideas to think about, at least

Other ideas:

a) place the markers even more above the ground and live with overlap on slopes

b) for markers near the cam (further away this might not be a problem) create an update control which from time to time picks the height on the quad end points from the terrain, then adjust the orientation of the quad points to be above the terrain

Wow, thank you all very much for the quick answers. :slight_smile:


I don’t really know how to handle the drawing order. But look at this thing, maybe it can help you out: projective-texture-mapping/

As t0neg0d said, I don't think this would be the best solution because a player can select armies with up to 50 units or so - I don't know if texture projection would be very efficient in this case. Altough it would be very, very nice to "lay" the marker on the terrain (With the same slopes etc.).

Other ideas:
a) place the markers even more above the ground and live with overlap on slopes
b) for markers near the cam (further away this might not be a problem) create an update control which from time to time picks the height on the quad end points from the terrain, then adjust the orientation of the quad points to be above the terrain

The thing is, that there ary very "extreme" slopes in my terrain (up to 60° or 70°), so just increasing the y-coordinate a little bit more wouldn't work always.

I think, I'll look into pspeed's API link. Maybe I can achieve that the marker is rendered at the end of the frame again (Or, what would be nicer: Don't render it in the normal process but afterwards). I'll tell you how it's going on. :)
@destroflyer said:
I think, I'll look into pspeed's API link. Maybe I can achieve that the marker is rendered at the end of the frame again (Or, what would be nicer: Don't render it in the normal process but afterwards). I'll tell you how it's going on. :)


I really think you're gonna waste a lot of time trying to get this to work this way. Even if you do find a solution that works in all instances (which is unfortunately doubtful, or the transparent/translucent render buckets wouldn't have the issues they do), the end effect is going to look a little bit less than desirable:

1. The rings are going to be flat no matter what the curve of the ground is. The steeper the curve, the worse it will look.
2. If you decide at some point to add a depth based effect (fog - shader based or filter) the rings are going to stick out like a soar thumb.

These are just off the top of my head... you'll run into more issues. Render order + any depth based effect will be the bane of your existence.

I would suggest looking at the alternatives mentioned here and find the one that has the least impact on performance with proper results.

Proper geometry sorting can work well in this case.



For example, render all of the ground first.



Render the markers with no depth test and no depth write.



Render everything else.



You can do this with viewports or you can do it with a GeometryComparator… since you’d still be rendering it in the normal opaque+transparent pass you don’t have to worry about post-processing or anything (actually because you control the rendering order you can render it as part of the regular opaque bucket but with blending on… I recommend this).



For a general solution, write a GeometryComparator that looks for a “layer” number on the Geometry’s user data. If the compared geometries have a layer number then compare them. If it’s the same layer number then let the default sorting work (you should be able to find an instance to the default opaque comparator). If one of the Geometries doesn’t have a layer then treat it as a top layer.

2 Likes
@pspeed said:
(actually because you control the rendering order you can render it as part of the regular opaque bucket but with blending on... I recommend this).


This is what I ended up doing... however, I still ran into problems with post filters and ended up converting the fog filter I posted here to shader based (again). There are just certain circumstances that no matter what I tried, I couldn't work around. In the case above... it looks (at first glance) like one of "those" situations.... and I was hoping to save him/her a ton of time by looking into alternate solutions that solve problems they might not have considered yet.

Yeah, I can’t help with hypothetical problems… so if he ends up having real issues then he should post them. In his case, if he avoids writing depth (could still test for it with some care) then it should have no affect on depth-based post effects since they will be operating on the depth of the terrain underneath.

Alright, I’ve created a subclass of the OpaqueComparator, so that I can use the default algorithm in case that none of the two geometries to compare are a “selection marker”. (Of course, I moved the selection markers from the transparent to the opaque bucket)

[java]import com.jme3.renderer.queue.OpaqueComparator;

import com.jme3.scene.Geometry;



public class MapGeometryComparator extends OpaqueComparator{



@Override

public int compare(Geometry geometry1, Geometry geometry2){

if(MaterialFactory.NAME_SELECTION_MARKER.equals(geometry1.getName())){

System.out.println(geometry1 + " - " + geometry2);

return 1;

}

else if(MaterialFactory.NAME_SELECTION_MARKER.equals(geometry2.getName())){

System.out.println(geometry1 + " - " + geometry2);

return -1;

}

return super.compare(geometry1, geometry2);

}

}[/java]



Little Question: Am i right, that a returned 1 means, that geometry1 should be rendered after geometry2? (This would make sense, so that the engine could just iterate over the created list of the comparator and render each item)



For test purposes, I named all the selection markers with MaterialFactory.NAME_SELECTION_MARKER, layer numbers and optimation (Check the cam if the unit is covered by a hill or unit and then don’t render the marker in the foreground) etc. will be added when a simple order customization works. So I tried to just always render the markers in foregound…



But… If I test this comparator by calling the following code, the terrain is still painted over some parts of the selection marker like in the image at the first post:

[java]viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Opaque, new MapGeometryComparator());[/java]

(The if’s are definitly called, since I get a lot of console output with the according geometries)



Do you know what I’m missing or am I doing completely wrong? :smiley:

1 Like
@destroflyer said:
Alright, I've created a subclass of the OpaqueComparator, so that I can use the default algorithm in case that none of the two geometries to compare are a "selection marker". (Of course, I moved the selection markers from the transparent to the opaque bucket)
[java]import com.jme3.renderer.queue.OpaqueComparator;
import com.jme3.scene.Geometry;

public class MapGeometryComparator extends OpaqueComparator{

@Override
public int compare(Geometry geometry1, Geometry geometry2){
if(MaterialFactory.NAME_SELECTION_MARKER.equals(geometry1.getName())){
System.out.println(geometry1 + " - " + geometry2);
return 1;
}
else if(MaterialFactory.NAME_SELECTION_MARKER.equals(geometry2.getName())){
System.out.println(geometry1 + " - " + geometry2);
return -1;
}
return super.compare(geometry1, geometry2);
}
}[/java]

Little Question: Am i right, that a returned 1 means, that geometry1 should be rendered after geometry2? (This would make sense, so that the engine could just iterate over the created list of the comparator and render each item)

For test purposes, I named all the selection markers with MaterialFactory.NAME_SELECTION_MARKER, layer numbers and optimation (Check the cam if the unit is covered by a hill or unit and then don't render the marker in the foreground) etc. will be added when a simple order customization works. So I tried to just always render the markers in foregound...

But.... If I test this comparator by calling the following code, the terrain is still painted over some parts of the selection marker like in the image at the first post:
[java]viewPort.getQueue().setGeometryComparator(RenderQueue.Bucket.Opaque, new MapGeometryComparator());[/java]
(The if's are definitly called, since I get a lot of console output with the according geometries)

Do you know what I'm missing or am I doing completely wrong? :D


Did you disable depth testing for your marker?
1 Like
Did you disable depth testing for your marker?

Hehe, that did the trick, thank you. :) I only disabled depth writing at this point.


Now I have to see, if I can apply the named optimations (Render after the unit, check if the unit is covered, ...) - If I run into problems, I'll post them in this topic.

Thanks again for your quick help. :)

EDIT: Hm, if I disable the depth test for the unit's geometries, it doesn't cast shadows anymore. :(
Any idea how to solve this another way? (Or how to check if the unit is covered by another object (I think, I could use the result of the default opaque comparator here in some way))

Are you going to try to make sure your unit is rendered after the circle?



You are using names right now but if it were me, I’d stick some user data on the geometry:

http://hub.jmonkeyengine.org/javadoc/com/jme3/scene/Spatial.html#setUserData(java.lang.String,%20java.lang.Object)



And then use that to bias the sort as I mentioned above. Then you could have terrain in layer 0, markers in layer 1, units in layer 2, etc… and they’d still be sorted properly amongst themselves.

@destroflyer said:
EDIT: Hm, if I disable the depth test for the unit's geometries, it doesn't cast shadows anymore. :(
Any idea how to solve this another way? (Or how to check if the unit is covered by another object (I think, I could use the result of the default opaque comparator here in some way))


No, don't disable depth test for the unit. You don't have to do that. Only the circle marker requires special treatment.

Yes, I deleted the according code.



Anyway - this looks somehow incredibly nice. :smiley: The unit is stil behind the hill but the marker isn’t. (The marker is always visible due to the disabled depth test - But the unit has still to pass the test and if it will be rendered, it appears before the marker (Unit-Layer: 2, Marker-Layer:1, Others: 0))



This way, the physical objects are rendered the right way and I can still see, where my unit is - Actually, I thought a marker in the foreground of a hill would look bad, but… I can only repeat myself - When moving around the unit, it looks great. :slight_smile: