3

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).

Not Rendering Fully

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.

Community
  • 1
  • 1
Lukehb
  • 454
  • 5
  • 15
  • You're supplying a RenderContext without using it, don't create RenderContext instead: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g3d/ModelBatch.html#ModelBatch(com.badlogic.gdx.graphics.g3d.utils.ShaderProvider) Also, use the DefaultShaderProvider: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g3d/utils/DefaultShaderProvider.html#DefaultShaderProvider(com.badlogic.gdx.graphics.g3d.shaders.DefaultShader.Config) – Xoppa Jan 05 '14 at 17:28
  • @Xoppa I have made your recommended changes, used the DefaultShaderProvider and let the ModelBatch handle the RenderContext. Unfortunately it has no effect on the terrain rendering fully. What's next to try? – Lukehb Jan 06 '14 at 00:58
  • I meant don't create the Shader yourself. Also, don't extend DefaultShaderProvider. Instead use `new DefaultShaderProvider(config)`. – Xoppa Jan 06 '14 at 01:16
  • Link is dead, please update – Winter Nov 04 '16 at 15:20

1 Answers1

4

Assuming your mesh is indexed, use:

meshPart.numVertices = chunk.getMesh().getNumIndices();

MeshPart#numVertices is the total amount of vertices to render, not the number of vertices in the mesh. See also: http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g3d/model/MeshPart.html

Xoppa
  • 7,983
  • 1
  • 23
  • 34