Drwaing on terrain

How can I draw on terrain?

For example, I want to draw circcle around unit representing its range (or even non circle range, that takes into account shooter and target altitude).

Or I want to draw “area” cursor on terrain, instead of 3d object that is put on terrain (which can look ugly, if there are high height diferences under cursor) or cursor on “screen” (which badly represents area).



But first, lets concetrate on drawing itself.



So far, I came up with 2 similar approaches

  1. draw another image on desired position

    Add texture, X,Y parameters to material and when computing pixel, substract X and Y from coordinates
  2. draw actual lines/arcs

    Add X,Y,R1^2,R2^2 and COLOR parameters and all pixels with R^2 between R1^2 and R2^2 (R is distance from X,Y) draw in COLOR (or current color modify by it, maybe take into account how far from circle edge it is)



    both of them I can do for one (or preselected few) drawing, but I want to be able to draw multiple marks on map.

    Using this approach, I need have float[] parameter, is it possible?



    What are drawbacks of this approach? Is there better approach?

Currently there isn’t a decal implementation in jme3, but we would love one that worked well. There are a few ways to go about doing this.



An old way, would be to create a mesh with the same resolution as the terrain that is the size you desire for your decal, then test each point in that mesh with the height of the terrain below it and adjust it so it is just above the terrain. This shouldn’t be an expensive operation and will be quite scalable for many decals. The only problem is the decal isn’t right on the terrain. You could set each point as the same height as the terrain and then render that mesh in the Transparent render queue, you would have to play with the depth testing/writing so you don’t get it drawing over other objects.



The other, more modern, way would be to modify the terrain shader, add a post process to it to draw a decal. This will probably yield better performance. I can provide some more details about this if you would like. Haven’t tested it though :slight_smile:

here is my experimental shader code:


uniform sampler2D m_Alpha;
uniform sampler2D m_Tex1;
uniform sampler2D m_Tex2;
uniform sampler2D m_Tex3;
uniform sampler2D m_TexMark;
uniform float m_Tex1Scale;
uniform float m_Tex2Scale;
uniform float m_Tex3Scale;
uniform float m_TexMarkScale;
uniform float m_TexMarkX;
uniform float m_TexMarkY;

varying vec2 texCoord;

void main(void)
{
vec2 xy;
vec2 mxy;
xy.x = texCoord.x-m_TexMarkX;
xy.y = texCoord.y-m_TexMarkY;
mxy.x = xy.x+1/32.0;
mxy.y = xy.y+1/32.0;
vec4 alpha = texture2D( m_Alpha, texCoord.xy );
vec4 tex0 = texture2D( m_Tex1, texCoord.xy * m_Tex1Scale ); // Tile
vec4 tex1 = texture2D( m_Tex2, texCoord.xy * m_Tex2Scale ); // Tile
vec4 tex2 = texture2D( m_Tex3, texCoord.xy * m_Tex3Scale ); // Tile
vec4 texM = texture2D( m_TexMark, mxy*m_TexMarkScale); // Tile
vec4 texR;
texR.r = 1;
texR.g = 0;
texR.b = 0;
texR.a = 1;

tex0 *= alpha.r; // Red channel
tex1 = mix( tex0, tex1, alpha.g ); // Green channel
tex2 = mix( tex1, tex2, alpha.b ); // Green channel
float r2 = xy.x*xy.x+xy.y*xy.y;
float rw = 0.02;
float rw2 = rw*rw;
float d = r2-rw2;
/// vec4 outColor = mix( tex1, tex2, alpha.b ); // Blue channel
vec4 outColor = mix(tex2,texM,texM.a);
if (d>-0.000001 && d<0.0001) {
outColor = mix(outColor,texR,0.6);
}
gl_FragColor = outColor;
}

There are some computations that could be moved out, and some hardcoded values that should be parameters, but thats not problem.

What I would like to know, is whether it can be scaled up to LOT of decals (of limited ammount of types), or can only support a few, and whether my approach is fudnamentaly wrong.

If it is not wrong, will JME3 in future contains better version of something like this?

Well, to get this to scale up, you’ll want to think about some things:



First, how many decals do you expect there to be per terrain patch? If this is going to be an RTS or something similar, then can you select 200 units and them all fit on the same terrain patch? Is it possible that the entire patch is covered with decals? Or is it going to be more like 3-5 decals per terrain patch?



Either way, you’ll want to recurse down the quad tree, putting only decal centers in the uniform array that are actually on that terrain patch (and yes, you can use arrays of floats or vec3’s or even structs as uniforms in glsl, though I’m not 100% sure how to load them yet with jME). Then, at the top of your shader, you’ll want to loop through the array and find which TexMark to use, then proceed as you have. I’m not exactly sure what you are doing with that TexMark texture. If you have it set to tile, then it would be tiling that texture over the whole patch. Maybe you have it set to clamp, and the edges are transparent, so you don’t see it, but you are doing unnecessary calculations. You should probably bring sampling and mixing the TexMark texture inside the if statement.



You may also want to consider using manhattan distance as a metric instead of euclidean distance to remove some unnecessary multiplies. Your selected areas will be square instead of circular, but you can put the color in the TexMark texture, and have its alpha channel fade out in a circular pattern.



However, using this approach means that every pixel has to look at every mark on the patch to find the closest one. If there are 200 marks on one patch, this means you are doing something like 1000 instructions per pixel… that won’t work. To make it work, you’ll need to store the positions in a Luminance/Alpha float texture, say 32x32 in size. Each pixel in the texture represents the closest mark on the TerrainPatch to the location that would be sampled by that pixel. Make sure to set the filter to nearest, no mipmap so that the values won’t get interpolated, and use a -1 or something like that to signify that there are no marks near enough to this texel to be included. Then, in your shader, just sample the texture using the terrainPatches texCoords, and you know what the closest decal is, and can go on from there as you have above.



This method will show the lines between the texels as two decals meeting and switching from one to the other at the same place, even as the decals move, when there are two decals close enough that they are in neighboring texels. You can deal with this by sampling all the neighboring texels from the texture as well, and mixing in the overlapping decals. You also have the problem of, what happens when two decals should occupy the same texel. You can solve this by increasing the resolution of the texture until it is not possible (assuming there is some sort of collision detection going on), or us an RGBA32F texture to store the 2 closest decals to each texel, or even 2 or 4 of them so you can store the 4 or 8 closest marks (use a 3D texture to only use one sampler). Then you wouldn’t have to sample neighboring texels. Depending on how detailed your marks are, at some point, the whole texel will just be filled with color, and it won’t really matter any more. Either way, with this technique you can have thousands of marks, and still only have a few samples and a few comparisons for each pixel. The only downside is the CPU-side filling of the texture and uploading each frame. It may even be worth it to use both methods, and only switch to the texture method when there are more than ~10 marks on the terrain patch.

Thank you. Looks like I’m on beging of usable path (but wit lot of dead ends) then :), so when i will actualy need, i can go further.