I have extended the LibGdx TerrainTest example and this Question so that it can render a collection of terrain chunks given some heightmap. This seems to work fine in GL10 as the example is using, however when I move the code over GL20 using the modelbatch and default shader the mesh does not render fully (See pic - in this pic we are trying to render one 32x32 chunk only).
This is the code for the application:
public class TerrainExperiment extends InputAdapter implements ApplicationListener {
private PerspectiveCamera camera;
private long lastTime = TimeUtils.nanoTime();
private Terrain terrain;
private ModelBatch modelBatch;
private DefaultShader mockShader;
@Override
public void create () {
terrain = new Terrain("testTerrain", 0, 0, "body", 32, 32, "137Industries",
new Flat[]{
//new Flat(0, 0, 10, 5, 5, ColorHelper.toHex(Color.MAGENTA), "yeaaah" ),
//new Flat(5,5, 5, 7, 7, ColorHelper.toHex(Color.BLACK), "yolo")
});
//setup shader for terrain
if(useGL20){
//this is default shader, except we turn face culling off
DefaultShader.Config config = new DefaultShader.Config();
//turn off lights and culling, just in case
config.defaultCullFace = 0;
config.defaultDepthFunc = GL20.GL_LEQUAL;
config.numPointLights = 0;
config.numDirectionalLights = 0;
mockShader = new DefaultShader(terrain.getModelInstance().getRenderable(new Renderable()), config);
mockShader.init();
modelBatch = new ModelBatch(new DefaultShaderProvider() {
@Override
public Shader getShader(Renderable renderable) {
return mockShader;
}
@Override
public void dispose() {
mockShader.dispose();
}
});
}
camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(-5f, 0, 0);
camera.lookAt(0, 0, 0);
camera.near = 0.1f;
camera.far = 300f;
camera.update();
}
@Override
public void resize(int width, int height) {
}
@Override
public void render () {
camera.update();
if(useGL20){
GL20 gl = Gdx.graphics.getGL20();
gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL20.GL_DEPTH_TEST);
//render the terrain model using the model batch
modelBatch.begin(camera);
if(terrain != null){
modelBatch.render(terrain.getModelInstance());
}
modelBatch.end();
}else{
GL10 gl = Gdx.graphics.getGL10();
gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL10.GL_DEPTH_TEST);
camera.apply(gl); //for gl10
//render meshes in the terrain model
for (Mesh mesh : terrain.getModelInstance().model.meshes) {
mesh.render(GL10.GL_TRIANGLES);
}
}
handleInput(Gdx.input, Gdx.graphics.getDeltaTime());
if (TimeUtils.nanoTime() - lastTime > 1000000000) {
Gdx.app.log("TerrainExperiment", "fps: " + Gdx.graphics.getFramesPerSecond());
lastTime = TimeUtils.nanoTime();
}
}
@Override
public void pause() { }
@Override
public void resume() { }
@Override
public void dispose() { }
private void handleInput (Input input, float delta) {
if (input.isTouched()) {
// simply modifying speed
delta = delta + 0.0485f;
// camera mouse look
// find vector 90° to me
Vector3 v90 = camera.direction.cpy();
Quaternion q = new Quaternion(camera.up, 90);
q.transform(v90);
// go to plane x,z
v90.y = 0;
// set rotation up/down
Quaternion qUpDown = new Quaternion(v90, Gdx.input.getDeltaY());
// set rotation left/right
Quaternion qLeftRight = new Quaternion(camera.up, -Gdx.input.getDeltaX());
// apply the rotations
qUpDown.transform(camera.direction);
qLeftRight.transform(camera.direction);
}
if (input.isKeyPressed(Keys.W)) {
Vector3 forward = new Vector3().set(camera.direction).scl(delta);
camera.position.add(forward);
}
if (input.isKeyPressed(Keys.S)) {
Vector3 backward = new Vector3().set(camera.direction).scl(delta);
camera.position.sub(backward);
}
if (input.isKeyPressed(Keys.A)) {
Vector3 left = new Vector3().set(camera.direction.cpy().crs(camera.up).nor()).scl(delta);
camera.position.sub(left);
}
if (input.isKeyPressed(Keys.D)) {
Vector3 right = new Vector3().set(camera.direction.cpy().crs(camera.up).nor()).scl(delta);
camera.position.add(right);
}
}
private static final boolean useGL20 = true; //change this to use GL10
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "BrowserThreeD";
cfg.useGL20 = useGL20;
cfg.width = 640;
cfg.height = 480;
new LwjglApplication(new TerrainExperiment(), cfg);
}
}
The code I am using to make the terrain chunk vertices and indices is directly copied from the SO question I mentioned above. You can see in the application code I have tried to turn off face culling too. I can only assume it must be a winding or duplicate vertex problem, but I can't see that it would draw so much of the chunk?
If this is relevant, this is how I making a libgdx model from the terrain chunk meshes:
Model result = new Model();
int chunkNum = 0;
for (TerrainChunk chunk : chunks) {
Material material = heightMap.getMaterial();
MeshPart meshPart = new MeshPart();
meshPart.id = "terrainChunk" + chunkNum;
meshPart.indexOffset = 0;
meshPart.numVertices = chunk.getMesh().getNumVertices();
meshPart.primitiveType = GL20.GL_TRIANGLES;
meshPart.mesh = chunk.getMesh();
NodePart nodePart = new NodePart();
nodePart.material = material;
nodePart.meshPart = meshPart;
Node node = new Node();
node.id = "terrainNode"+chunkNum;
node.parts.add(nodePart);
result.meshes.add(chunk.getMesh());
result.materials.add(material);
result.nodes.add(node);
result.meshParts.add(meshPart);
result.manageDisposable(chunk.getMesh());
chunkNum++;
}
What's going on? I can post of a link to full working source if needed.