0
\$\begingroup\$

I decided to give the ModelBuilder a try but I cannot get rid of the smoothing of my terrain. So what I tried:

  • Create each triangle individually with there own vertex position.
  • Put each triangle in it's own part.
  • Put each triangle in its own node.
  • Set indices individually after vertex positions been set.
  • Used ensureTriangleIndices and ensureCapacity to set the vertex and index count.

Executable test

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.Vector3;

/**
 * libgdxtestenvironment [2019]
 * By Menno Gouw
 */
public class TerrainBuilderTest extends ApplicationAdapter {

    PerspectiveCamera cam;
    CameraInputController camController;
    ModelBatch modelBatch;
    Model model;
    ModelInstance instance;
    Environment environment;

    //CreateTerrain createTerrain;
    // Hardcoded heightmap for testing
    private final int[][] heightMap = {
            {0, 1, 3, 1, 0},
            {1, 2, 1, 3, 1},
            {2, 3, 2, 2, 1},
            {2, 4, 2, 1, 2},
            {1, 3, 2, 1, 2},
    };

    @Override
    public void create () {
        modelBatch = new ModelBatch();
        setEnvironment();

        setCamera();

        //createTerrain = new CreateTerrain(16, 16);
        instance = new ModelInstance(myModelBuilder(heightMap));

        Gdx.input.setInputProcessor(camController = new CameraInputController(cam));
    }

    private void setCamera() {
        cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        cam.position.set(0f, 7f, 10f);
        cam.lookAt(0, 0, 0);
        cam.near = 1f;
        cam.far = 50f;
        cam.update();
    }

    private void setEnvironment() {
        environment = new Environment();
        environment.set(new ColorAttribute(ColorAttribute.AmbientLight, .4f, .4f, .4f, 1f));
        environment.add(new DirectionalLight().set(1, 1, 1, -.2f, -.7f, -.1f));
    }

    @Override
    public void render () {
        camController.update();

        Gdx.gl.glViewport(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight());
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

        modelBatch.begin(cam);
        modelBatch.render(instance, environment);
        modelBatch.end();
    }

    @Override
    public void dispose () {
        modelBatch.dispose();
        model.dispose();
    }

    private Model myModelBuilder(int[][] heightMap){
        ModelBuilder modelBuilder = new ModelBuilder();

        modelBuilder.begin();
        MeshPartBuilder mpb = null;

        // Build the terrain, I tried with one part/node and with one for each triangle.
        modelBuilder.node();
        for (int y = 0; y < heightMap[0].length - 1; y++){
            for (int x = 0; x < heightMap.length - 1; x++) {
                mpb = modelBuilder.part("quad[" + x + "," + y + "]" + "tri-1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.ColorUnpacked,
                        new Material(ColorAttribute.createDiffuse(Color.WHITE)));
                mpb.triangle(
                        new Vector3(x, heightMap[x][y] / 2f, y),
                        new Vector3(x, heightMap[x][y + 1] / 2f, y + 1),
                        new Vector3(x + 1, heightMap[x + 1][y] / 2f, y)
                );

                modelBuilder.node();
                mpb = modelBuilder.part("quad[" + x + "," + y + "]" + "tri-2", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.ColorUnpacked,
                        new Material(ColorAttribute.createDiffuse(Color.WHITE)));
                mpb.triangle(
                        new Vector3(x + 1, heightMap[x + 1][y] / 2f, y),
                        new Vector3(x, heightMap[x][y + 1] / 2f, y + 1),
                        new Vector3(x + 1, heightMap[x + 1][y + 1] / 2f, y + 1)
                );
                modelBuilder.node();
            }
        }

        // Try setting indices manually
        for (int i = 0; i < (heightMap[0].length - 1) * (heightMap.length - 1); i += 3){
            mpb.triangle((short)i, (short)(i + 1),(short)(i + 2));
        }

        // Try setting indice numbers
        mpb.ensureTriangleIndices((heightMap[0].length - 1) * (heightMap.length - 1) * 2);

        mpb.ensureCapacity(
                (heightMap[0].length - 1) * (heightMap.length - 1) * 6,
                (heightMap[0].length - 1) * (heightMap.length - 1) * 6
        );

        return modelBuilder.end();
    }
}
\$\endgroup\$
0
\$\begingroup\$

I solved the issue by using VertexInfo but would still like a answer where I can provide raw floats straight into the MeshPartBuilder. I Also find it strange that in the example code in my question I provide 6 verts per quad and the mesh still gets smoothed, it probably merges the verts under the hood otherwise it could not smooth the mesh afaik.

Anyway, with VerteInfo I can specify all parameters.

            v1.setPos(x + 1, heightMap[x + 1][y] / 2f, y);
            v2.setPos(x, heightMap[x][y + 1] / 2f, y + 1);
            v3.setPos(x + 1, heightMap[x + 1][y + 1] / 2f, y + 1);
            v1.setUV(1, 0);
            v2.setUV(0, 1);
            v3.setUV(1, 1);
            v1.setNor(normal);
            v2.setNor(normal);
            v3.setNor(normal);

And then just provide the MeshPartBuilder with the VertexInfo objects.

mpb.triangle(v1, v2, v3);

For completeness, one does need to specify the part consist of these attributes.

mpb = modelBuilder.part("part", 
                    GL20.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates,
                    new Material(TextureAttribute.createDiffuse(texture)));

And it is also worth noting that you should not create a new VertexInfo object for each vertex. I just initialized 3 and reused them for each triangle.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.