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).
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
[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+(.2fi);
// Green value (is reduced by .2 on each next vertex)
colorArray[colorIndex++]= c4.y; //0.9f-(0.2fi);
// 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]