Apply 6 Textures to randomized Terrain using a generated alphaMap?

Ok this will be my last post on the forums, but i really need this answered. I have been sitting here for the last 2 days, and my head is about to explode.

I have created a randomized Heightmap, and i have generated 2 AlphaMaps from this HeightMap using the following JMonkey Plugin:

from Thread:

http://hub.jmonkeyengine.org/forum/topic/heightmap-based-alpha-map/

Here are the results:

alpha1

http://postimg.org/image/7aa0uzwjd/

alpha2

http://postimg.org/image/erj8a7m2h/

The generator takes in the following arguments:

Texture1Height
Texture2Height
Texture3Height
MaxHeight

ARGUMENTS FOR ALPHA 1:

alphaGen.setMaxHeight(35.0f);
alphaGen.setTex1Height(0.0f);
alphaGen.setTex2Height(20.0f);
alphaGen.setTex3Height(30.0f);

ARGUMENTS FOR ALPHA 2:

alphaGen.setMaxHeight(70.0f);
alphaGen.setTex1Height(35.0f)
alphaGen.setTex2Height(40.0f);
alphaGen.setTex3Height(50.0f);

I am changing the max height so that Alpha 1 does not generate anything at a certain height, so that i can have 3 Textures for each half (height wise) of the terrain.

Now as i understand it, when i have two separate AlphaMaps, i can create up to 6 diffuse textures for my terrain. (Acording to the following Documentation):

(See section: Texture Splatting)
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:terrain

So i applied both of the alphaMaps that were generated, to the material as follows:

mat_terrain.setTexture(“AlphaMap”, alpha1);
mat_terrain.setTexture(“AlphaMap_1″, alpha2);

So now the Terrain material has 2 AlphaMaps. So now the last thing i think i need to do, is add the diffuse textures:

    Material mat_terrain = new Material(assetManager,
            "Common/MatDefs/Terrain/TerrainLighting.j3md");
    mat_terrain.setBoolean("useTriPlanarMapping", false);
    
    Texture blnk = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    blnk.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap", blnk);
    mat_terrain.setFloat("DiffuseMap_0_scale", 128);

    Texture dirt = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    dirt.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap", dirt);
    mat_terrain.setFloat("DiffuseMap_1_scale", 128);

    Texture grass = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    grass.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_1", grass);
    mat_terrain.setFloat("DiffuseMap_2_scale", 128);

    Texture path = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    path.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_2", path);
    mat_terrain.setFloat("DiffuseMap_3_scale", 128);

    Texture road = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Road.png");
    road.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_5", road);
    mat_terrain.setFloat("DiffuseMap_4_scale", 128);

    Texture rock = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Stone.png");
    rock.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_4", rock);
    mat_terrain.setFloat("DiffuseMap_5_scale", 128);

    Texture snow = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Snow.png");
    rock.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_3", snow);
    mat_terrain.setFloat("DiffuseMap_6_scale", 128);

I added the blnk Diffuse at the start, because as i understand it, the first AlphaMap allows for 4 diffuse maps.

When i run this, it seems that Alpha 2 is completely overriding Alpha 1.

My questions are:

> Have i made any mistakes?
> Is this the correct way to apply 6 diffuse maps to a terrain
> What is the right way?
> Is there any further documentation?

I cannot thank you enough for your Time, and i truly appreciate all of your efforts in advance.

:slight_smile:

Thank You!

1 Like

Since i cant edit the main post, ill do it here:

A lot of the Texture loading etc is The same code over and over, so its not much to read.
Ill explain this in steps, and in the order that they occur in:

[java]public void createTerrain() {
//Future: Generate Heightmap
Material mat_terrain = new Material(assetManager,
“Common/MatDefs/Terrain/TerrainLighting.j3md”);
mat_terrain.setBoolean(“useTriPlanarMapping”, false);

    Texture blnk = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    blnk.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap", blnk);
    mat_terrain.setFloat("DiffuseMap_0_scale", 128);

    Texture dirt = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    dirt.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_1", dirt);
    mat_terrain.setFloat("DiffuseMap_1_scale", 128);

    Texture grass = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    grass.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_2", grass);
    mat_terrain.setFloat("DiffuseMap_2_scale", 128);

    Texture path = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Grass.png");
    path.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_3", path);
    mat_terrain.setFloat("DiffuseMap_3_scale", 128);

    Texture road = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Road.png");
    road.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_4", road);
    mat_terrain.setFloat("DiffuseMap_4_scale", 128);

    Texture rock = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Stone.png");
    rock.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_5", rock);
    mat_terrain.setFloat("DiffuseMap_5_scale", 128);

    Texture snow = assetManager.loadTexture(
            "Textures/Terrain/Diffuse/Snow.png");
    rock.setWrap(WrapMode.Repeat);
    mat_terrain.setTexture("DiffuseMap_6", snow);
    mat_terrain.setFloat("DiffuseMap_6_scale", 128);

    //mat_terrain.getAdditionalRenderState().setWireframe(true);
    TerrainCreator terrainCreator = new TerrainCreator(1024, 65, getCamera(), mat_terrain);
    terrain = terrainCreator.getTerrain();

    terrain.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
    rootNode.attachChild(terrain);
}[/java]

This method is responsible for loading the textures, and passing them into My terrain Generator. The terrain generator handles the input parameters to generate a heightMap, and an alpha Map based on that heightMap:

[java]public class TerrainCreator {

private TerrainQuad terrain;
Material mat_terrain;
BufferedImage heightAdjuster;
BufferedImage waterHeight;
HeightBasedAlphaMapGenerator alphaGen;

public TerrainCreator(int size, int patchsize, Camera cam, Material mat) {
    mat_terrain = mat;
    int patchSizeA = patchsize;
    AbstractHeightMap heightmap = null;
    heightAdjuster = null;
    waterHeight = null;

    double[] data = Noise.normalize(Noise.blend(Noise.perlinNoise(size, size, Main.randInt(7, 10)), Noise.turbulence(size, size, Main.randInt(105, 120)), 1.75));
    for (int i = 0; i < data.length; i++) {
        data[i] = 255 * data[i];
    }
    BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_BYTE_GRAY);
    img.getRaster().setPixels(0, 0, size, size, data);

    Texture tex1 = Main.assetManager.loadTexture("Textures/Terrain/Adjusters/HeightAdjuster1.png");
    heightAdjuster = ImageToAwt.convert(tex1.getImage(), false, true, 0);

    Texture tex2 = Main.assetManager.loadTexture("Textures/Terrain/Water/Water1.png");
    waterHeight = ImageToAwt.convert(tex2.getImage(), false, true, 0);

    //Initialize alphaT with random Texture
    Texture alphaT = Main.assetManager.loadTexture("Textures/Terrain/AlphaMaps/Alpha1.png");

    //Check if Loaded Image is eual to size

    BufferedImage terUpd = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);

    Graphics g = terUpd.getGraphics();
    g.drawImage(img, 0, 0, null);
    g.drawImage(heightAdjuster, 0, 0, null);
    g.drawImage(waterHeight, 0, 0, null);

    AWTLoader loader = new AWTLoader();
    Image load = loader.load(terUpd, true);

    heightmap = new ImageBasedHeightMap(load, 0.5f);
    heightmap.load();

    heightmap.smooth(0.85f, 5); //Smooth Heightmap

    alphaGen = new HeightBasedAlphaMapGenerator(heightmap);

    Image alphaImg;
    alphaGen.setMaxHeight(35.0f);
    alphaGen.setTex1Height(0.0f);
    alphaGen.setTex2Height(20.0f);
    alphaGen.setTex3Height(30.0f);
    alphaImg = alphaGen.renderAlphaMap();
    alphaT.setImage(alphaImg);
    mat_terrain.setTexture("AlphaMap", alphaT);

    alphaGen.setMaxHeight(100.0f);
    alphaGen.setTex1Height(35.0f);
    alphaGen.setTex2Height(40.0f);
    alphaGen.setTex3Height(50.0f);
    alphaImg = alphaGen.renderAlphaMap();
    alphaT.setImage(alphaImg);
    mat_terrain.setTexture("AlphaMap_1", alphaT);

    //Generate only 1024 Sized HeightMaps in Generator and tile
    terrain = new TerrainQuad("Terrain", patchSizeA, size + 1, heightmap.getHeightMap());

    terrain.setMaterial(mat_terrain);
    //terrain.setCullHint(Spatial.CullHint.Never);
    terrain.setLocalTranslation(
            0, -40, 0);
    terrain.setLocalScale(
            2.0f, 1.0f, 2.0f); // Scale y to make less steep
    terrain.setModelBound(new BoundingBox());
    terrain.updateModelBound();
    TerrainLodControl control = new TerrainLodControl(terrain, cam);
    control.setLodCalculator(new DistanceLodCalculator(patchsize, 0.975f)); 
    terrain.addControl(control);
}[/java] 

I have cut out some irrelevant bits of the code to shorten the reading time. The water and height adjuster textures are simply overlays over the generated heightmaps: Rivers and Mountains.

Now, as explained above, my issue is that i apply both alphaMaps inside of my TerrainCreator class (2nd code block) but somehow the second one is overriding the first one.

The alphaGen object simply takes in a heightMap, and generates alpha maps based on the input parameters:

[java]Image alphaImg;
alphaGen.setMaxHeight(35.0f);
alphaGen.setTex1Height(0.0f);
alphaGen.setTex2Height(20.0f);
alphaGen.setTex3Height(30.0f);
alphaImg = alphaGen.renderAlphaMap();
alphaT.setImage(alphaImg);
mat_terrain.setTexture(“AlphaMap”, alphaT);

    alphaGen.setMaxHeight(100.0f);
    alphaGen.setTex1Height(35.0f);
    alphaGen.setTex2Height(40.0f);
    alphaGen.setTex3Height(50.0f);
    alphaImg = alphaGen.renderAlphaMap();
    alphaT.setImage(alphaImg);
    mat_terrain.setTexture("AlphaMap_1", alphaT);[/java] 

And the heightMap itself. Here is the Generator Method inside of the AlphaMap Class:

[java] public Image renderAlphaMap() {
if (rendered) {
return alphamap;
}

    int height = heightmap.getSize();
    int width = heightmap.getSize();

    ByteBuffer data = BufferUtils.createByteBuffer(width * height * 4);

    for (int x = 0; x < width; x++) {
        for (int z = 0; z < height; z++) {

            int alpha = 255, red = 0, green = 0, blue = 0;

            float pointHeight = heightmap.
                    getScaledHeightAtPoint(z, width - (x + 1));

            //GENERATE RGB BASED ON HEIGHT
            if (pointHeight < maxHeight) {
                if (pointHeight > tex3Height) {
                    blue = 255;
                } else if (pointHeight > tex2Height) {
                    green = 255;
                } else if (pointHeight > tex1Height) {
                    red = 255;
                } else if (pointHeight < tex1Height) {
                    alpha = 0;
                }
            } else {
                alpha = 0;
            }
            data.
                    put((byte) red).
                    put((byte) green).
                    put((byte) blue).
                    put((byte) alpha);
        }
    }

    alphamap = new Image(Image.Format.RGBA8, width, height, data);
    rendered = true;
    return alphamap;
}[/java] 

This process returns 2 Alpha Maps:

alpha1

http://postimg.org/image/7aa0uzwjd/

alpha2

http://postimg.org/image/erj8a7m2h/

Which are then applied as shown above int the parameters code snippet.

ISSUE: again, the alpha maps are not showing properly, what i want, is for the terrain to display 6 different textures, however it is displaying 3 and a black texture on lower parts of the terrain, See Alpha Results for details.

Thank you for your time. I posted all the code you requested with all of the information.

1 Like

It might be worth posting the actual code used to setup the material. (And post it inside proper code blocks so that we can read it… see the icons above the edit box.)

For example, the snippets above set the alpha textures before the material is even created. We can’t see how they were loaded or when/where/how they were set exactly. It makes it difficult to point out simple errors that may have been overlooked.

2 Likes

Here is an Image of what is happening:

As you can see, the Terrain has the RGB values of the second alpha map, and then the B value of the first, but the RG values of the first Alpha do not show.

1 Like

Im having the same exact problem lol. Wish i could help man :confused:

Yeah i havent figured it out yet man. Ill post and answer as soon as i do. The documentation is very much lacking on this topic, so il draft something when i get an answer. Its been two nights so far :stuck_out_tongue:

I started a thread on GameDev Stack exchange, someone there might be able to help us out:

http://gamedev.stackexchange.com/questions/70213/jmonkey-engine-using-2-alpha-maps-for-texture-splatting

Always a good place for answers :slight_smile:

A single alpha map is responsible for the visibility of four diffuse maps one for each channel of your RGBA image. From your description it looks like you aren’t considering the alpha layer.
The terrain shader renders each layer on top of the other, in the following order:
alphamap.r DiffuseMap
AlphaMap.g DiffuseMap_1
AlphaMap.b DiffuseMap_2
AlphaMap.a DiffuseMap_3
AlphaMap_1.r DiffuseMap_4
AlphaMap_1.g DiffuseMap_5
…
and so on.
I hope this helps.

1 Like