Problem: multi texturing voxels, terrain splatting

Hiya folks,



I took Moonkeys simple voxel engine found here…http://hub.jmonkeyengine.org/groups/free-announcements/forum/topic/simple-voxel-engine-starter-kit/ and started doing some modifications to it. First problem I had was applying a cliff texture to it, based on the heightmap. I solved this by splitting the terrain into 2 meshes, one for the cliffs and one for the grass and the expense of a slower frame rate (2 sets of vectors, 2 sets of texcoords) (this suits me fine because the maps i will use will be about one quarter the size of the heightmap supplied).



Photobucket



My current problem is trying to add support for multiple textures to the grass mesh. If you have read Moonkeys code you will see that a set of texture coords are created for each voxel…so when I tried adding texture splatting to the grass mesh, the alpha map drew a copy of itself on each individual voxel, and not the whole mesh, which of course, looks like crap. So I have a few questions…



Is the Terrain.j3md definition only useful for TerrainQuads?

Is there a way to apply an alpha map over the whole mesh?

Is there a way to add textured areas to the grass mesh without making new meshes for the new textures?

Any advice is appreciated.



Also as a note, the map will be static once it is generated, so during the actual games run time, it wont be dynamically updated. This voxel engine just provided the look I wanted to achieve.



Heres the code…it might look a little ugly but it does the job. Sorry for all the nulls :stuck_out_tongue:



[java]

/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.

    /

    package mygame;



    //import java.util.Map;



    import com.jme3.asset.AssetManager;

    import com.jme3.terrain.heightmap.AbstractHeightMap;

    import java.util.ArrayList;



    import com.jme3.material.Material;

    import com.jme3.math.ColorRGBA;

    import com.jme3.math.Vector2f;



    import com.jme3.math.Vector3f;

    import com.jme3.math.Vector4f;

    import com.jme3.scene.Geometry;

    import com.jme3.scene.Mesh;

    import com.jme3.scene.Node;

    import java.util.Random;



    import com.jme3.scene.VertexBuffer.Type;

    import com.jme3.terrain.heightmap.ImageBasedHeightMap;

    import com.jme3.texture.Texture;

    import com.jme3.util.BufferUtils;

    import jme3tools.converters.ImageToAwt;





    public class Map extends Node

    {



    AbstractHeightMap heightmap = null;



    public static float DEFAULT_BLOCK_SIZE = 0.5f;



    int lengthX = 128;

    int lengthY = 128;

    int lengthZ = 128;

    int mapCells[][][] = new int[lengthX][lengthY][lengthZ];

    int lightCells[][][] = new int[lengthX][lengthY][lengthZ];

    Mesh mapmesh = new Mesh();

    Mesh mapmesh2 = new Mesh();



    int rightFace = 0;

    int leftFace = 1;

    int topFace = 2;

    int bottomFace = 3;

    int frontFace = 4;

    int backFace = 5;



    ArrayList<Vector3f> vertices = new ArrayList<Vector3f>(); // points grass

    ArrayList<Vector3f> vertices2 = new ArrayList<Vector3f>(); // points cliffs

    ArrayList<Vector3f> normals = new ArrayList<Vector3f>(); // normals

    ArrayList<Vector2f> texCoord = new ArrayList<Vector2f>(); // tex cords grass

    ArrayList<Vector2f> texCoord2 = new ArrayList<Vector2f>(); // tex coords cliff

    ArrayList<Integer> indexes = new ArrayList<Integer>(); // indexes





    ArrayList<Vector4f> verticesColor = new ArrayList<Vector4f>();



    //String matDefName = "Common/MatDefs/Misc/ShowNormals.j3md";

    //String matDefName = "Common/MatDefs/Light/Lighting.j3md";

    String matDefName = "Common/MatDefs/Misc/Unshaded.j3md";

    String matDefName1 = "Common/MatDefs/Terrain/Terrain.j3md";





    float bsize = 1; // block size



    AssetManager assetManager;



    public Map(AssetManager assetManager)

    {

    this.assetManager = assetManager;

    Texture heightMapImage = assetManager.loadTexture("Textures/heightmap.jpg");

    heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, false, 0));

    heightmap.load();

    testHeightMap();

    loadMapData();

    build();

    }



    private void testHeightMap()

    {

    /


    for(int x = 0; x < lengthX; x++ )

    {

    for (int z = 0; z < lengthZ; z++)

    {

    System.out.print("; ");

    System.out.print("x: "+x+" z:"+z+" ->"+heightmap.getTrueHeightAtPoint(x, z));

    }

    System.out.print("n");

    }

    */

    }



    public void initMyMap()

    {

    for (int z = 0; z < lengthZ; z++) {

    for(int x = 0; x < lengthX; x++ ) {

    for (int y = 0; y < lengthY; y++) {

    mapCells[x][y][z] = 1;

    }

    }

    }

    }



    private void loadMapData()

    {

    for (int z = 0; z < lengthZ; z++) {

    for(int x = 0; x < lengthX; x++ ) {

    for (int y = 0; y < (heightmap.getTrueHeightAtPoint(x, z)0.08f); y++) {

    /


    System.out.print("x: "+x+" ");

    System.out.print("y: "+y+" ");

    System.out.print("z: "+z+"n");

    /

    mapCells[x][y][z] = 1;

    }

    }

    }

    }



    public void cleanEdgeCells()

    {

    for (int x = 0; x < lengthX; x++) {

    for (int y = 0; y < lengthY; y++) {

    mapCells[x][y][0] = 0;

    mapCells[x][y][lengthZ-1] = 0;

    mapCells[x][2][1] = 0;

    mapCells[x][2][2] = 0;

    }

    }



    for (int z = 0; z < lengthZ; z++) {

    for (int y = 0; y < lengthY; y++) {

    mapCells[0][y][z] = 0;

    mapCells[lengthX-1][y][z] = 0;

    }

    }





    }



    public void initLightCells()

    {

    for (int z = 0; z < lengthZ; z++)

    {

    for (int x = 0; x < lengthX; x++)

    {

    for (int y = 0; y < lengthY; y++)

    {

    lightCells[x][y][z] = 0;

    }

    }

    }

    }



    public void castDownTheLight()

    {

    initLightCells();

    for (int z = 0; z < lengthZ; z++)

    {

    for (int x = 0; x < lengthX; x++)

    {

    for (int y = 0; y < lengthY; y++)

    {

    if(mapCells[x][y][z] == 1)

    {

    break;

    }

    lightCells[x][y][z] = 1;

    }

    }

    }

    }



    public void addRandomCells()

    {

    Random r = new Random();

    byte blocktype;



    for (int y = 0; y < lengthY; y++) {

    for (int z = 0; z < lengthZ; z++) {

    for (int x = 0; x < lengthX; x++) {

    blocktype = 1; // default

    //if(y < 3) blocktype = 1; // base

    if(mapCells[x][y][z] == 1)

    {

    if(r.nextInt(15) == 0)

    {

    blocktype = 0;

    }

    mapCells[x][y][z] = blocktype;

    }

    //System.out.print("mapCells["+x+"]["+y+"]["+z+"]:"+mapCells[x][y][z]+"n");

    }

    }

    }

    }



    public void build()

    {

    for (int z = 0; z < lengthZ; z++) {

    for (int x = 0; x < lengthX; x++) {

    for (int y = 0; y < lengthY; y++) {

    if(mapCells[x][y][z] == 1)

    {

    createWalls(checkSix(x,y,z), 0.0f+(x), 0.0f+(y), 0.0f+(z));

    }

    }

    }

    }



    Vector4f[] c4 = verticesColor.toArray(new Vector4f[verticesColor.size()]);



    int colorIndex = 0;

    float[] colorArray = new float[verticesColor.size()4];

    //Set custom RGBA value for each Vertex. Values range from 0.0f to 1.0f

    for(int i = 0; i < verticesColor.size(); i++){

    // Red value (is increased by .2 on each next vertex here)

    colorArray[colorIndex++]= c4.x; //0.1f+(.2f
    i);

    // Green value (is reduced by .2 on each next vertex)

    colorArray[colorIndex++]= c4.y; //0.9f-(0.2f
    i);

    // Blue value (remains the same in our case)

    colorArray[colorIndex++]= c4.z;

    // Alpha value (no transparency set here)

    colorArray[colorIndex++]= c4.w;

    }





    Vector3f[] v3 = vertices.toArray(new Vector3f[vertices.size()]);

    Vector3f[] v3c = vertices2.toArray(new Vector3f[vertices2.size()]);

    /Vector4f[] c4 = verticesColor.toArray(new Vector4f[verticesColor.size()]);/

    Vector3f[] n3 = normals.toArray(new Vector3f[normals.size()]);

    Vector2f[] v2 = texCoord.toArray(new Vector2f[texCoord.size()]);

    Vector2f[] v2c = texCoord2.toArray(new Vector2f[texCoord2.size()]);



    int indx[] = convertIntegers(indexes);







    mapmesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(v3));

    mapmesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(n3));

    mapmesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(v2));

    mapmesh.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indx));

    mapmesh.setBuffer(Type.Color, 4, colorArray);



    //System.out.print("test: "+c4[0].w);



    mapmesh2.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(v3c));

    mapmesh2.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(n3));

    mapmesh2.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(v2c));

    mapmesh2.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indx));

    mapmesh2.setBuffer(Type.Color, 4, colorArray);



    mapmesh.updateBound();

    mapmesh2.updateBound();



    // Creating a geometry, and apply a single color material to it

    Geometry levelGeom = new Geometry("OurMesh", mapmesh);

    Geometry levelGeom2 = new Geometry("OurMesh2", mapmesh2);



    Material mat1 = new Material(assetManager, matDefName1);

    Material mat2 = new Material(assetManager, matDefName);



    Texture alpha = assetManager.loadTexture("Textures/alpha.png");

    Texture grass = assetManager.loadTexture("Textures/grass.png");

    grass.setWrap(Texture.WrapMode.Repeat);

    Texture dirt = assetManager.loadTexture("Textures/dirt.jpg");

    dirt.setWrap(Texture.WrapMode.Repeat);

    Texture road = assetManager.loadTexture("Textures/road.jpg");

    road.setWrap(Texture.WrapMode.Repeat);



    mat1.setTexture("Alpha", alpha);

    mat1.setTexture("Tex1", grass);

    mat1.setTexture("Tex2", dirt);

    mat1.setTexture("Tex3", road);



    mat2.setTexture("ColorMap", assetManager.loadTexture("Textures/cliff.png"));











    levelGeom.setMaterial(mat1);

    levelGeom2.setMaterial(mat2);

    this.attachChild(levelGeom);

    this.attachChild(levelGeom2);

    }





    private void createWalls(int faces[], float x, float y, float z)

    {

    int verticesSize = vertices.size();

    float halfbsize = bsize/2;

    float bx, by, bz;

    bx = x * bsize;

    by = y * bsize;

    bz = z * bsize;



    for (int i = 0; i < faces.length; i++) {

    //System.out.print("face["+i+"]: "+faces+"n");

    }



    Vector3f pa = new Vector3f(bx-halfbsize, by-halfbsize, bz+halfbsize);

    Vector3f pb = new Vector3f(bx+halfbsize, by-halfbsize, bz+halfbsize);

    Vector3f pc = new Vector3f(bx-halfbsize, by+halfbsize, bz+halfbsize);

    Vector3f pd = new Vector3f(bx+halfbsize, by+halfbsize, bz+halfbsize);



    Vector3f pe = new Vector3f(bx-halfbsize, by-halfbsize, bz-halfbsize);

    Vector3f pf = new Vector3f(bx+halfbsize, by-halfbsize, bz-halfbsize);

    Vector3f pg = new Vector3f(bx-halfbsize, by+halfbsize, bz-halfbsize);

    Vector3f ph = new Vector3f(bx+halfbsize, by+halfbsize, bz-halfbsize);



    Vector3f normalUp = new Vector3f(0, 1, 0);

    Vector3f normalDown = new Vector3f(0, -1, 0);

    Vector3f normalRight = new Vector3f(1, 0, 0);

    Vector3f normalLeft = new Vector3f(-1, 0, 0);

    Vector3f normalFront = new Vector3f(0, 0, 1);

    Vector3f normalBack = new Vector3f(0, 0, -1);



    Vector2f t1 = new Vector2f(0, 0);

    Vector2f t2 = new Vector2f(1.0f, 0);

    Vector2f t3 = new Vector2f(0, 1.0f);

    Vector2f t4 = new Vector2f(1.0f, 1.0f);



    // x = +

    if(faces[rightFace] == 1)

    {

    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices2.add(pb);

    vertices2.add(pf);

    vertices2.add(pd);

    vertices2.add(ph);

    normals.add(normalRight);

    normals.add(normalRight);

    normals.add(normalRight);

    normals.add(normalRight);

    texCoord2.add(t1);

    texCoord2.add(t2);

    texCoord2.add(t3);

    texCoord2.add(t4);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    indexes.add(verticesSize+2);

    indexes.add(verticesSize+0);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+3);

    indexes.add(verticesSize+2);



    //vertex colors

    verticesColor.add(new Vector4f(.5f, .5f, .5f, 1f));

    verticesColor.add(new Vector4f(.5f, .5f, .5f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    }



    // x = -

    if(faces[leftFace] == 1)

    {

    verticesSize = vertices.size();



    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices2.add(pe);

    vertices2.add(pa);

    vertices2.add(pg);

    vertices2.add(pc);

    normals.add(normalLeft);

    normals.add(normalLeft);

    normals.add(normalLeft);

    normals.add(normalLeft);

    texCoord2.add(t1);

    texCoord2.add(t2);

    texCoord2.add(t3);

    texCoord2.add(t4);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    indexes.add(verticesSize+2);

    indexes.add(verticesSize+0);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+3);

    indexes.add(verticesSize+2);



    verticesColor.add(new Vector4f(.5f, .5f, .5f, 1f));

    verticesColor.add(new Vector4f(.5f, .5f, .5f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    }

    //y = +

    if(faces[topFace] == 1)

    {

    verticesSize = vertices.size();



    vertices.add(pc);

    vertices.add(pd);

    vertices.add(pg);

    vertices.add(ph);

    vertices2.add(null);

    vertices2.add(null);

    vertices2.add(null);

    vertices2.add(null);

    normals.add(normalUp);

    normals.add(normalUp);

    normals.add(normalUp);

    normals.add(normalUp);



    texCoord.add(t1);

    texCoord.add(t2);

    texCoord.add(t3);

    texCoord.add(t4);



    texCoord2.add(null);

    texCoord2.add(null);

    texCoord2.add(null);

    texCoord2.add(null);

    indexes.add(verticesSize+2);

    indexes.add(verticesSize+0);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+3);

    indexes.add(verticesSize+2);



    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    }

    //y = -

    if(faces[bottomFace] == 1)

    {

    verticesSize = vertices.size();



    vertices.add(pe);

    vertices.add(pf);

    vertices.add(pa);

    vertices.add(pb);

    vertices2.add(null);

    vertices2.add(null);

    vertices2.add(null);

    vertices2.add(null);

    normals.add(normalDown);

    normals.add(normalDown);

    normals.add(normalDown);

    normals.add(normalDown);



    texCoord.add(t1);

    texCoord.add(t2);

    texCoord.add(t3);

    texCoord.add(t4);



    texCoord2.add(null);

    texCoord2.add(null);

    texCoord2.add(null);

    texCoord2.add(null);

    indexes.add(verticesSize+2);

    indexes.add(verticesSize+0);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+3);

    indexes.add(verticesSize+2);



    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    }



    if(faces[frontFace] == 1)

    {

    verticesSize = vertices.size();



    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices2.add(pa);

    vertices2.add(pb);

    vertices2.add(pc);

    vertices2.add(pd);

    normals.add(normalFront);

    normals.add(normalFront);

    normals.add(normalFront);

    normals.add(normalFront);

    texCoord2.add(t1);

    texCoord2.add(t2);

    texCoord2.add(t3);

    texCoord2.add(t4);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    indexes.add(verticesSize+2);

    indexes.add(verticesSize+0);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+3);

    indexes.add(verticesSize+2);



    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    }



    if(faces[backFace] == 1)

    {

    verticesSize = vertices.size();



    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices.add(null);

    vertices2.add(pf);

    vertices2.add(pe);

    vertices2.add(ph);

    vertices2.add(pg);

    normals.add(normalBack);

    normals.add(normalBack);

    normals.add(normalBack);

    normals.add(normalBack);

    texCoord2.add(t1);

    texCoord2.add(t2);

    texCoord2.add(t3);

    texCoord2.add(t4);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    texCoord.add(null);

    indexes.add(verticesSize+2);

    indexes.add(verticesSize+0);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+1);

    indexes.add(verticesSize+3);

    indexes.add(verticesSize+2);



    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(.25f, .25f, .25f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    verticesColor.add(new Vector4f(1f, 1f, 1f, 1f));

    }

    }



    private int[] checkSix(int x, int y, int z)

    {

    //System.out.println("checkSix: " + x + ", " + y + ", " + z);



    int faces[] = new int[6];



    for (int i = 0; i < faces.length; i++) {

    faces = 1;

    }



    // right face

    if (indexExists(lengthX, (x+1)))

    {

    if(mapCells[x+1][y][z] == 1){ faces[0] = 0; }

    }

    //System.out.println("right face: "+faces[0]);



    // left face

    //int xD = x-1;

    if (indexExists(lengthX, x-1))

    {

    if(mapCells[x-1][y][z] == 1){ faces[1] = 0; }

    }

    //System.out.println("left face: "+faces[1]);



    // top face

    if (indexExists(lengthY, y+1))

    {

    if(mapCells[x][y+1][z] == 1){ faces[2] = 0; }

    }

    //System.out.println("top face: "+faces[2]);



    // bottom face

    if (indexExists(lengthY, y-1))

    {

    if(mapCells[x][y-1][z] == 1){ faces[3] = 0; }

    }

    //System.out.println("bottom face: "+faces[3]);

    // back face

    if (indexExists(lengthZ, z+1))

    {

    if(mapCells[x][y][z+1] == 1){ faces[4] = 0; }

    }

    //System.out.println("back face: "+faces[4]);

    // front face

    if (indexExists(lengthZ, z-1))

    {

    if(mapCells[x][y][z-1] == 1){ faces[5] = 0; }

    }

    //System.out.println("front face: "+faces[5]);

    return faces;

    }





    public boolean indexExists(int size, int index)

    {

    boolean result = false;

    if (index >= 0 && index < size){

    result = true;

    }

    return result;

    }





    public static int[] convertIntegers(ArrayList<Integer> integers)

    {

    int[] ret = new int[integers.size()];

    for (int i=0; i < ret.length; i++)

    {

    ret = integers.get(i).intValue();

    }

    return ret;

    }





    }



    [/java]

Terrain.j3md is just a simple shader, similar to Unshaded but with splatting. TerrainLighting.j3md has TriPlanarMapping which will help you texture steep slopes (cliffs). All materials can be used on any geometry.

Thanks for the reply,

is there a way to have the alpha map span the whole geometry without having to change the texcoords of each voxel? Im guessing thats what my issue is, the material is applying the alphamap to each set of texcoords (256x256 sets). Im not sure how to define 1 set of coordinates for the whole geometry though since its not exactly a flat quad its drawing to.

That is essentially what tri-planar mapping does, it diverges from using texture coordinates.

Here is a paper on it so you can get a feel for what it is trying to achieve. (look at section 1.5)

Since you are using blocks (voxels) you don’t have to use the blending that the shader uses, but probably no need to take it out unless it improves performance.

Hey thats wild, cant wait to try it out…when i get home from work lol

Alright! I got it going using TerrainLighting.j3md and tri-plannar mapping. However, the issue I have now is that the alpha map isnt bound to the edges of my mesh. I edited the alpha map to draw a few pixels in each corner a different color for a different texture, and it changed the whole mesh to that one texture, which leads me to think the the alpha map has scaled itself a bazillion times (scientific termonology there ;)) and only a small corner of it is actually covering the mesh.



So my next question, how can i scale the alpha map, or make it confine itself to the limits of my mesh?

both the heightmap and alpha map are 256x256

Ok, further research into this tells me that the alpha map is no scaled to world coordinates in place of local coordinates, again, is there anyway to limit the alpha map to drawing over my mesh and not scaling to the whole world space?

Correct, the alpha map is using the texture coordinates. You will have to modify the shader to make it use the world coordinates as the the splat textures are (you will probably have to adjust its scale too).

Alright, I have copied the TerrainLighting definition, .vert and .frag files into my asset manager, renamed them SauceShader, made appropriate name changes in the definition, tested it, and it works identical to terrain lighting. Now, i read the code in length and crammed some OpenGL language, and I can see in the fragment file several places where coords have been applied to alphamaps using tri-planar mapping. Great. Now the problem I have now is that I have no idea what exactly it is i should do to scale the alpha map. Clearly, I havent had to customise an OpenGL shader before

Huzzah! I have solved my problem.



http://i18.photobucket.com/albums/b104/Manix_Theking/voxelsplat.png



There is a line in TerrainLighting.frag, under the block



[java]#ifdef TRI_PLANAR_MAPPING[/java]



and



[java]vec4 calculateTriPlanarDiffuseBlend(in vec3 wNorm, in vec4 wVert, in vec2 texCoord)[/java]



that looks like this…



[java]vec4 alphaBlend = texture2D( m_AlphaMap, texCoord.xy );[/java]



change the last parameter to…



[java]vec4 alphaBlend = texture2D( m_AlphaMap, coords.xz/256);[/java]



where the number (256, in this case) is the size of the mesh you want it to draw over. the .xz is important. Image dimensions dont matter, because they will adjust to fit. this essentially means the higher res your image, the more detail you can get. this screenshot is from a 256x256 alphamap…and its pretty ugly, but i have tested it on 1024x1024 and it looks great.



If the mesh is smaller or bigger, then the number has to change. I possibly need to edit the material definition to include a means fixing this.



Im going to tidy my wee terrain system up and post it once i am happy :smiley:

2 Likes

Cool! nicely done.