ive got something worth showing now i think also for you guys to critique!
anyway some things i realized are messed up are, at the terrains seams you can clearly see it. I need to fix this by making sure at the edge of the touching terrains the normals are pointing in the same direction. and also i realize it crashes if you walk off the edge.
some info:
any terrain not withing reach of 1 of the current terrain you’re on is detached (not including diagnols )
there terrains texturing is built up in layers of type, using vertex alpha, my example shows grass and dirt.
heres some screenshots:
http://www.thewoops.com/bgilb/VTscreenshot1.PNG
http://www.thewoops.com/bgilb/VTscreenshot2.PNG
code:
VertexTerrainBlock.java
package VertexTerrain;
import com.jme.scene.Node;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.MaterialState;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.util.TextureManager;
//import com.jme.util.BumpMapColorController;
import com.jme.util.geom.BufferUtils;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.jme.system.DisplaySystem;
import java.util.ArrayList;
import java.net.URL;
import java.nio.FloatBuffer;
import javax.swing.*;
/**
* Created by IntelliJ IDEA.
* User: bglib
* Date: Feb 20, 2007
* Time: 3:47:16 AM
* To change this template use File | Settings | File Templates.
*/
public class VertexTerrainBlock extends Node
{
private ArrayList<TextureState> textureStates = new ArrayList<TextureState>();
public ArrayList<TerrainBlock> terrainBlocks = new ArrayList<TerrainBlock>();
private int terrainSize;
private boolean terrainClod;
private int numLayers=0;
private FloatBuffer vertexBuffer;
//private BumpMapColorController bumpMapCon;
private int[] heights;
String name="";
private int gridX;
private int gridY;
public VertexTerrainBlock(String name, int size, boolean clod, DisplaySystem display)
{
this.name=name;
terrainSize=size;
terrainClod=clod;
AlphaState as = display.getRenderer().createAlphaState();
as.setBlendEnabled(true);
as.setSrcFunction(AlphaState.SB_SRC_ALPHA);
as.setDstFunction(AlphaState.SB_ONE_MINUS_SRC_ALPHA);
as.setTestFunction(AlphaState.TF_GREATER);
MaterialState ms = display.getRenderer().createMaterialState();
ms.setColorMaterial(MaterialState.CM_DIFFUSE);
this.setRenderState(ms);
this.setRenderState(as);
randomNoise();
}
public void randomNoise()
{
int[] noiseRandom = new int[(terrainSize*terrainSize)];
for(int x = 0;x<((terrainSize)*(terrainSize));x++)
{
//noiseRandom[x]=(int)(Math.random()*1.5);
noiseRandom[x]=0;
}
heights=noiseRandom;
}
public void setGridPos(int x, int y)
{
gridX=x;
gridY=y;
}
public int getGridX()
{
return gridX;
}
public int getTerrainSize()
{
return terrainSize;
}
public int getGridY()
{
return gridY;
}
public void setHeightFromMap(String loc)
{
URL grayImage=VertexTerrainBlock.
class.getClassLoader().
getResource(loc);
ImageBasedHeightMap ib=new ImageBasedHeightMap(
new ImageIcon(grayImage).getImage());
ib.load();
for (TerrainBlock tb : terrainBlocks)
{
tb.setHeightMap(ib.getHeightMap());
System.out.println(ib.getHeightMap());
System.out.println(tb.getHeightMap());
tb.setModelBound(new BoundingBox());
tb.updateModelBound();
tb.updateRenderState();
tb.updateGeometricState(0, true);
tb.updateWorldData(0);
tb.updateFromHeightMap();
}
this.setModelBound(new BoundingBox());
this.updateModelBound();
this.updateRenderState();
this.updateGeometricState(0,true);
this.updateWorldData(0);
}
public float[] getColorBuffer(float alpha)
{
float[] tempFloat = new float[(terrainSize*terrainSize)*4];
for(int x = 0;x<((terrainSize)*(terrainSize))*4;x+=4)
{
tempFloat[x]=1f;
tempFloat[x+1]=1f;
tempFloat[x+2]=1f;
tempFloat[x+3]=alpha;
}
return tempFloat;
}
public void addLayer(String texture, DisplaySystem display, float startAlpha)
{
TerrainBlock tb=new TerrainBlock(name+ ", block: "+numLayers,terrainSize,
new Vector3f(8,1,8),
heights,
new Vector3f(0,0,0),
terrainClod);
tb.setModelBound(new BoundingBox());
tb.updateModelBound();
tb.updateRenderState();
Texture tex2 =
TextureManager.loadTexture(VertexTerrainBlock.class.getClassLoader().getResource(
texture),
Texture.MM_LINEAR,
Texture.FM_LINEAR);
tex2.setScale(new Vector3f(terrainSize,terrainSize,terrainSize));
tex2.setWrap(Texture.WM_WRAP_S_WRAP_T);
// Assign the texture to the TextureState
TextureState ts=display.getRenderer().createTextureState();
ts.setTexture(tex2);
//ts.setTexture(tex2,1);
textureStates.add(ts);
FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(getColorBuffer(startAlpha));
tb.setColorBuffer(0,colorBuffer);
tb.setRenderState(ts);
terrainBlocks.add(tb);
this.attachChild(tb);
numLayers++;
}
public float getHeight(Vector3f vec)
{
float[] pos = new float[3];
vec.toArray(pos);
int size = (terrainSize-1)*8;
int x = (int) ((pos[0])-(gridX*size))/8;//8 is the scale amount
int y = (int) ((pos[2])-(gridY*size))/8;
terrainBlocks.get(0).updateFromHeightMap();
//System.out.println("Name: "+(int)vertexBuffer.get(1+(x+(terrainSize*y))*3));
FloatBuffer vertexBuffer = terrainBlocks.get(0).getVertexBuffer(0);
return vertexBuffer.get(1+(x+(terrainSize*y))*3);
}
public void setHeight(int x, int y, float amount)
{
for (TerrainBlock tb : terrainBlocks)
{
vertexBuffer = tb.getVertexBuffer(0);
vertexBuffer.put(1 + (x + (terrainSize * y)) * 3, amount);
tb.setHeightMapValue(x, y, (int) amount);
tb.setVertexBuffer(0, vertexBuffer);
tb.setModelBound(new BoundingBox());
tb.updateModelBound();
tb.updateRenderState();
tb.updateGeometricState(0, true);
//tb.updateFromHeightMap();
}
}
public void setAlpha(int x, int y, int layer, float amount)
{
TerrainBlock tb = terrainBlocks.get(layer);
FloatBuffer colorBuffer = tb.getColorBuffer(0);//reuse same buffer for speed
colorBuffer.put(3+(x+(terrainSize*y))*4, amount);
//colorBuffer.put(4, amount);
tb.setColorBuffer(0,colorBuffer);
tb.setModelBound(new BoundingBox());
tb.updateModelBound();
tb.updateRenderState();
tb.updateGeometricState(0,true);
}
}
SeamlessVertexTerrain.java
package VertexTerrain;
import com.jme.scene.Node;
import com.jme.math.Vector3f;
import com.jme.bounding.BoundingBox;
import java.util.ArrayList;
/**
* Created by IntelliJ IDEA.
* User: bglib
* Date: Feb 23, 2007
* Time: 12:22:09 AM
* To change this template use File | Settings | File Templates.
*/
public class SeamlessVertexTerrain extends Node
{
ArrayList<VertexTerrainBlock> terrains = new ArrayList<VertexTerrainBlock>();
int curPosX=0;
int curPosY=0;
int widthX=0;
int widthY=0;
long updateDelay=System.currentTimeMillis();
/*
0 1 2
3 4 5
6 7 8
*/
public SeamlessVertexTerrain()
{
this.setName("SeamlessTerrain");
}
public void addChild(int pos, VertexTerrainBlock vtb, int terrainSize, int x, int y)//8 is the scale size
{
terrains.add(pos, vtb);
vtb.setGridPos(x,y);
if(x>widthX)
widthX=x;
if(y>widthY)
widthY=y;
//float amnt=32;
vtb.setLocalTranslation(new Vector3f(((terrainSize-1)*8)*x,0,((terrainSize-1)*8)*y));
//this.attachChild(vtb);
this.setModelBound(new BoundingBox());
this.updateModelBound();
this.updateRenderState();
this.updateGeometricState(0,true);
this.updateWorldData(0);
}
public void update(int delay)
{
if(System.currentTimeMillis()-updateDelay>=delay)
{
for(int x=0;x<widthX+2;x++)
{
for(int y=0;y<widthY+2;y++)
{
if(!(x==curPosX && y==curPosY) ||
!(x==curPosX-1 && y==curPosY-1) ||
!(x==curPosX && y==curPosY-1) ||
!(x==curPosX+1 && y==curPosY-1) ||
!(x==curPosX-1 && y==curPosY) ||
!(x==curPosX+1 && y==curPosY) ||
!(x==curPosX-1 && y==curPosY+1) ||
!(x==curPosX && y==curPosY+1) ||
!(x==curPosX+1 && y==curPosY+1))
{
VertexTerrainBlock vtb = getCurrentSector(x,y);
if(this.hasChild(vtb))
this.detachChild(vtb);
}
if((x==curPosX && y==curPosY) ||
(x==curPosX-1 && y==curPosY-1) ||
(x==curPosX && y==curPosY-1) ||
(x==curPosX+1 && y==curPosY-1) ||
(x==curPosX-1 && y==curPosY) ||
(x==curPosX+1 && y==curPosY) ||
(x==curPosX-1 && y==curPosY+1) ||
(x==curPosX && y==curPosY+1) ||
(x==curPosX+1 && y==curPosY+1))
{
VertexTerrainBlock vtb = getCurrentSector(x,y);
if(!this.hasChild(vtb))
{
this.attachChild(vtb);
this.setModelBound(new BoundingBox());
this.updateModelBound();
this.updateRenderState();
this.updateGeometricState(0,true);
this.updateWorldData(0);
}
}
}
}
updateDelay=System.currentTimeMillis();
}
}
public VertexTerrainBlock getCurrentSector(int x, int y)
{
for (VertexTerrainBlock terrain : terrains)
{
int x2 = terrain.getGridX();
int y2 = terrain.getGridY();
if( (x==x2) && (y==y2) )
{
return terrain;
}
}
return null;
}
public VertexTerrainBlock getCurrentSectorVTB(Vector3f position)
{
float[] pos = new float [3];
position.toArray(pos);
int x= (int)((pos[0])/248);
int y= (int)((pos[2])/248);
curPosX= (int)((pos[0])/248);
curPosY= (int)((pos[2])/248);
for (VertexTerrainBlock terrain : terrains)
{
int x2 = terrain.getGridX();
int y2 = terrain.getGridY();
if( (x==x2) && (y==y2) )
{
return terrain;
}
}
return null;
}
/*public TerrainBlock equals(Node n)
{
VertexTerrainBlock vtb1=null;
for(int x=0;x<terrains.size();x++)
{
vtb1 = terrains.get(x);
if(n.hasChild(vtb1.terrainBlocks.get(0)))
{
return vtb1.terrainBlocks.get(0);
}
}
return null;
}*/
}
Also for convience i uploaded the height maps and textures to this folder:
http://www.thewoops.com/bgilb/terrain