Demo3D2 source

Below is the single source file to the basic demonstration in Java of how to build a 3D mesh out of multiple objects and render it in a single call.

Optimising render calls like this reduces overhead and speeds things up a hell of a lot. Which is essential when you need every CPU clock cycle to process a shed load more information.

You can find the actual demo in action in HTML5 HERE (Demo3D2)…

This is using libGdx and it runs on desktop, android and in a web browser.

And it’s as simple as it gets.

package com.wlgfx.demo3d2;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
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.attributes.FloatAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.environment.PointLight;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;

public class Demo3d2 extends ApplicationAdapter {
    
	ModelBatch modelBatch;
	SpriteBatch batch;
	Texture img;
	PerspectiveCamera cam;
	Environment environment;
	Model model;
	Texture texture;
	ModelInstance gridInstance;
	Material mat;
	PointLight light;
	
	float camx = 0, camy = 0, camz = 0;
	float camxs = 10, camys = 15, camzs = 20;
	float camxr = 65, camyr = 75, camzr = 55;
	
	@Override
	public void create () {
		batch = new SpriteBatch();
		img = new Texture("badlogic.jpg");
		modelBatch = new ModelBatch();
		texture = new Texture("metal.jpg");
		
		environment = new Environment();
		environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
		environment.add(new DirectionalLight().set(0.8f,  0.8f,  0.8f, -1f, -0.8f, -0.2f));
		
		light = new PointLight().set(1, 1, 1, 0, 0, 0, 50);
		environment.add(light);
		
		cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        cam.position.set(10f, 10f, 10f);
        cam.lookAt(0,0,0);
        cam.near = 0f;
        cam.far = 1000f;
        cam.update();
        
        mat = new Material();
        mat.set(TextureAttribute.createDiffuse(texture));
        mat.set(TextureAttribute.createBump(texture));
        mat.set(ColorAttribute.createSpecular(1, 1, 1, 1));
        mat.set(FloatAttribute.createShininess(8f));
        
        model = buildGrid();
        
        gridInstance = new ModelInstance(model);
        
        Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST);
        Gdx.gl20.glDepthFunc(GL20.GL_GREATER);
        Gdx.gl20.glDepthMask(true);
        Gdx.gl20.glDepthRangef(0.0f, 100.0f);
        Gdx.gl20.glClearDepthf(1.0f);
	}
	
	private Model buildGrid() {
		mat = new Material();
        mat.set(TextureAttribute.createDiffuse(texture));
        mat.set(TextureAttribute.createBump(texture));
        mat.set(ColorAttribute.createSpecular(1, 1, 1, 1));
        mat.set(FloatAttribute.createShininess(8f));
        
		ModelBuilder builder = new ModelBuilder();
		builder.begin();
		
		MeshPartBuilder partBuilder = builder.part("meshX", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal | Usage.TextureCoordinates | Usage.BiNormal, mat);
		
		int step = 20;
		
		for (int a = -100; a <= 100; a += step) {
			for (int b = -100; b <= 100; b += step) {
				for (int c = -100; c <= 100; c += step * 4) {
					partBuilder.box(a, b, c, 1, 1, step * 4);
					partBuilder.box(a, c, b, 1, step * 4, 1);
					partBuilder.box(c, a, b, step * 4, 1, 1);
				}
			}
		}
		
		model = builder.end();
		
		return model;
	}
	
	private void updateCamera(float delta) {
		camx = (camx + camxs * delta) % 360.0f;
		camy = (camy + camys * delta) % 360.0f;
		camz = (camz + camzs * delta) % 360.0f;
		
		cam.position.x = (float)Math.sin(Math.toRadians(camx)) * camxr;
		cam.position.y = (float)Math.cos(Math.toRadians(camy)) * camyr;
		cam.position.z = (float)Math.sin(Math.toRadians(camz)) * camzr;
		
		cam.lookAt(0, 0, 0);
		
		light.setPosition(cam.position.x, cam.position.y, cam.position.z);
		
		cam.update();
	}

	@Override
	public void render () {
		Gdx.gl.glClearColor(1, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
		
		batch.begin();
		batch.draw(img, 0, 0);
		batch.end();
		
		float delta = Gdx.graphics.getDeltaTime();
		updateCamera(delta);
		
		modelBatch.begin(cam);
		modelBatch.render(gridInstance, environment);
		modelBatch.end();
	}
}